def banner(text, type=1, width=35): """Function to print *text* to output file in a banner of minimum width *width* and minimum three-line height for *type* = 1 or one-line height for *type* = 2. """ lines = text.split("\n") max_length = 0 for line in lines: if len(line) > max_length: max_length = len(line) max_length = max([width, max_length]) null = "" if type == 1: banner = " //" + null.center(max_length, ">") + "//\n" for line in lines: banner += " //" + line.center(max_length) + "//\n" banner += " //" + null.center(max_length, "<") + "//\n" if type == 2: banner = "" for line in lines: banner += (" " + line + " ").center(max_length, "=") psi4.print_out(banner)
def compare_vectors(expected, computed, digits, label): """Function to compare two vectors. Prints :py:func:`util.success` when elements of vector *computed* match elements of vector *expected* to number of *digits*. Performs a system exit on failure to match symmetry structure, dimension, or element values. Used in input files in the test suite. """ if (expected.nirrep() != computed.nirrep()): print("\t%s has %d irreps, but %s has %d\n." % (expected.name(), expected.nirrep(), computed.name(), computed.nirrep())) sys.exit(1) nirreps = expected.nirrep() for irrep in range(nirreps): if(expected.dim(irrep) != computed.dim(irrep)): print("\tThe reference has %d entries in irrep %d, but the computed vector has %d\n." % (expected.dim(irrep), irrep, computed.dim(irrep))) sys.exit(1) dim = expected.dim(irrep) failed = 0 for entry in range(dim): if(abs(expected.get(irrep, entry) - computed.get(irrep, entry)) > 10 ** (-digits)): print("\t%s: computed value (%s) does not match (%s)." % (label, computed.get(irrep, entry), expected.get(irrep, entry))) failed = 1 break if(failed): psi4.print_out("The computed vector\n") computed.print_out() psi4.print_out("The reference vector\n") expected.print_out() sys.exit(1) success(label)
def __init__(self, circs): if circs[5] == '': msg = """{0}: Method '{1}' with {2} '{3}' and REFERENCE '{4}' not available{5}""".format(*circs) else: msg = """{0}: Method '{1}' with {2} '{3}' and REFERENCE '{4}' not directable to QC_MODULE '{5}'""".format(*circs) PsiException.__init__(self, msg) self.message = msg psi4.print_out('\nPsiException: %s\n\n' % (msg))
def compute_energy(self): uhf = self.uhf e, nocc, norb, gmo = self.uhf.e, self.uhf.nocc, self.uhf.norb, self.gmo Ec = 0.0 for i in range(nocc): for j in range(nocc): for a in range(nocc, norb): for b in range(nocc, norb): Ec += (1/4.0) * gmo[i, j, a, b]**2 / (e[i]+e[j]-e[a]-e[b]) self.E = uhf.E + Ec psi4.print_out('MP2 E Ec\n') psi4.print_out(' {:20.15f} {:20.15f}'.format(self.E, Ec)) return Ec
def extract_sowreap_from_output(sowout, quantity, sownum, linkage, allvital=False): """Function to examine file *sowout* from a sow/reap distributed job for formatted line with electronic energy information about index *sownum* to be used for construction of *quantity* computations as directed by master input file with *linkage* kwarg. When file *sowout* is missing or incomplete files, function will either return zero (*allvital* is ``False``) or terminate (*allvital* is ``True``) since some sow/reap procedures can produce meaningful results (database) from an incomplete set of sown files, while others cannot (gradient, hessian). """ E = 0.0 try: freagent = open('%s.out' % (sowout), 'r') except IOError: if allvital: raise ValidationError('Aborting upon output file \'%s.out\' not found.\n' % (sowout)) else: ValidationError('Aborting upon output file \'%s.out\' not found.\n' % (sowout)) return 0.0 else: while True: line = freagent.readline() if not line: if E == 0.0: if allvital: raise ValidationError('Aborting upon output file \'%s.out\' has no %s RESULT line.\n' % (sowout, quantity)) else: ValidationError('Aborting upon output file \'%s.out\' has no %s RESULT line.\n' % (sowout, quantity)) break s = line.split() if (len(s) != 0) and (s[0:3] == [quantity, 'RESULT:', 'computation']): if int(s[3]) != linkage: raise ValidationError('Output file \'%s.out\' has linkage %s incompatible with master.in linkage %s.' % (sowout, str(s[3]), str(linkage))) if s[6] != str(sownum + 1): raise ValidationError('Output file \'%s.out\' has nominal affiliation %s incompatible with item %s.' % (sowout, s[6], str(sownum + 1))) if (s[8:10] == ['electronic', 'energy']): E = float(s[10]) psi4.print_out('%s RESULT: electronic energy = %20.12f\n' % (quantity, E)) freagent.close() return E
def fitGeneral(self): """Function to perform a general fit of diffuse charges to wavefunction density. """ psi4.print_out(" => Diffuse Charge Fitting (Determines da) <=\n\n") self.wfn = psi4.wavefunction() self.Da = self.wfn.Da() self.basis = self.wfn.basisset() parser = psi4.Gaussian94BasisSetParser() self.ribasis = psi4.BasisSet.construct(parser, self.molecule, "DF_BASIS_SCF") fitter = psi4.DFChargeFitter() fitter.setPrimary(self.basis) fitter.setAuxiliary(self.ribasis) fitter.setD(self.Da) self.da = fitter.fit() self.da.scale(2.0)
def compute_energy(self): """ Compute the MP2 energy :return: MP2 energy """ ndocc = self.ndocc nbf = self.nbf gmo = self.gmo e = self.e E = 0.0 for i in range(ndocc): for j in range(ndocc): for a in range(ndocc, nbf): for b in range(ndocc, nbf): E += gmo[i, a, j, b]*(2*gmo[i, a, j, b] - gmo[i, b, j, a])/\ (e[i] + e[j] - e[a] - e[b]) psi4.print_out('@MP2 correlation energy: {:20.15f}\n'.format(E)) psi4.print_out('@Total MP2 energy: {:20.15f}\n'.format(E + self.ehf)) self.emp2 = E
def _print_nbody_energy(energy_body_dict, header): psi4.print_out("""\n ==> N-Body: %s energies <==\n\n""" % header) psi4.print_out(""" n-Body Total Energy [Eh] I.E. [kcal/mol] Delta [kcal/mol]\n""") previous_e = energy_body_dict[1] nbody_range = energy_body_dict.keys() nbody_range.sort() for n in nbody_range: delta_e = (energy_body_dict[n] - previous_e) delta_e_kcal = delta_e * p4const.psi_hartree2kcalmol int_e_kcal = (energy_body_dict[n] - energy_body_dict[1]) * p4const.psi_hartree2kcalmol psi4.print_out(""" %4s %20.12f %20.12f %20.12f\n""" % (n, energy_body_dict[n], int_e_kcal, delta_e_kcal)) previous_e = energy_body_dict[n] psi4.print_out("\n")
def compute_energy(self): S = np.matrix(la.block_diag(self.S, self.S)) T = np.matrix(la.block_diag(self.T, self.T)) V = np.matrix(la.block_diag(self.V, self.V)) D = np.matrix(np.zeros((self.nsbf, self.nsbf))) X = np.matrix(la.inv(la.sqrtm(S))) h = T + V E0 = 0 for count in range(self.maxiter): F = h + self.vu Ft = X * F * X e, Ct = la.eigh(Ft) C = X * np.matrix(Ct) OC = np.matrix(C[:,:self.nelec]) D = OC*OC.T self.vu = np.einsum('upvq,qp->uv', self.G, D) E1 = np.sum((np.array(h) + 0.5 * np.array(self.vu))*np.array(D.T)) + self.V_nuc psi4.print_out('Iteration {:<d} {:.10f} {:.10f}\n'.format(count, E1, E1-E0)) if abs(E1 - E0) < self.e_convergence: psi4.print_out('\nFinal HF Energy: {:<5.10f}'.format(E1)) self.C = C self.epsi = e self.ehf = E1 break else: E0 = E1 else: psi4.print_out('\n:( Does not converge :(')
def compute_energy(self): """ Compute the rhf energy :return: energy """ X = np.matrix(la.inv(la.sqrtm(self.S))) D = np.matrix(np.zeros((self.nbf, self.nbf))) h = self.T + self.V E0 = 0 for count in range(self.maxiter): F = h + self.vu Ft = X * F * X e, Ct = la.eigh(Ft) C = X * np.matrix(Ct) DOC = np.matrix(C[:,:self.ndocc]) D = DOC*DOC.T G = 2*self.g - self.g.swapaxes(2,3) self.vu = np.einsum('upvq,qp->uv', G, D) E1 = np.sum((2 * np.array(h) + np.array(self.vu))*np.array(D.T)) + self.V_nuc psi4.print_out('Iteration {:<d} {:.10f} {:.10f}\n'.format(count, E1, E1-E0)) if abs(E1 - E0) < self.e_convergence: psi4.print_out('\nFinal Energy: {:<5.10f}'.format(E1)) break else: E0 = E1 else: psi4.print_out('\n:( Does not converge :(')
def fitScf(self): """Function to run scf and fit a system of diffuse charges to resulting density. """ basisChanged = psi4.has_option_changed("BASIS") ribasisChanged = psi4.has_option_changed("DF_BASIS_SCF") scftypeChanged = psi4.has_option_changed("SCF_TYPE") basis = psi4.get_option("BASIS") ribasis = psi4.get_option("DF_BASIS_SCF") scftype = psi4.get_option("SCF_TYPE") psi4.print_out(" => Diffuse SCF (Determines Da) <=\n\n") activate(self.molecule) psi4.set_global_option("BASIS", self.basisname) psi4.set_global_option("DF_BASIS_SCF", self.ribasisname) psi4.set_global_option("SCF_TYPE", "DF") energy('scf') psi4.print_out("\n") self.fitGeneral() psi4.clean() psi4.set_global_option("BASIS", basis) psi4.set_global_option("DF_BASIS_SCF", ribasis) psi4.set_global_option("SCF_TYPE", scftype) if not basisChanged: psi4.revoke_option_changed("BASIS") if not ribasisChanged: psi4.revoke_option_changed("DF_BASIS_SCF") if not scftypeChanged: psi4.revoke_option_changed("SCF_TYPE")
def compute_energy(self): """ Compute the rhf energy :return: energy """ V_nuc, T, S, V, g = self.V_nuc, self.T, self.S, self.V, self.g nbf, ndocc = self.nbf, self.ndocc D = np.zeros((self.nbf, self.nbf)) C = np.zeros((self.nbf, self.nbf)) # S^{-1/2} X = la.inv(la.sqrtm(S)).view(np.matrix) psi4.print_out('Iter. Energy\n') scf_form = '{: >3d} {: >20.15f}\n' i = 0 converged = False e_old = 0 # Begin SCF iterations while not converged and i < self.maxiter: F = self.build_fock(D) # Transformed Fock matrix to an orthogonal basis tF = X*F*X # Diagonalized the transformed Fock matrix e, tC = la.eigh(tF) # Convert eigenvectors to AO basis (from orthogonal basis) C = X*tC # Form density matrix D = C[:, :ndocc]*C[:, :ndocc].T # Compute the energy E = np.trace((F + T +V)*D) + V_nuc i += 1 # Check convergence if abs(E - e_old) < self.e_convergence: converged = True e_old = E psi4.print_out(scf_form.format(i, E)) psi4.print_out('RHF Energy: {:20.15f}\n'.format(E)) self.e = e self.C = C self.E = E return E
def compute_energy(self): """ Compute the rhf energy :return: energy """ T, V, h, S, X = self.T, self.V, self.h, self.S, self.X V_nuc, nocc = self.V_nuc, self.nocc # Use an empty density guess D = np.zeros(T.shape) psi4.print_out('\nIter. Energy\n') scf_form = '{: >3d} {: >20.15f}\n' i = 0 converged = False e_old = 0 # Begin SCF iterations while not converged and i < self.maxiter: # build the Fock with the previous density f = self.build_fock(D) # Transform the Fock matrix to an orthogonal basis tf = X*f*X # Diagonalize the transformed Fock matrix e, tC = la.eigh(tf) # Convert eigenvectors to AO basis (from orthogonal basis) C = X*tC # Form density matrix D = C[:, :nocc]*C[:, :nocc].T # Compute the energy E = np.trace((h/2 + f/2)*D) + V_nuc i += 1 # Check convergence if abs(E - e_old) < self.e_convergence: converged = True e_old = E psi4.print_out(scf_form.format(i, E)) psi4.print_out('RHF Energy: {:20.15f}\n'.format(E)) self.E = E self.C = C self.e = e return E
def compare_matrices(expected, computed, digits, label): """Function to compare two matrices. Prints :py:func:`util.success` when elements of matrix *computed* match elements of matrix *expected* to number of *digits*. Performs a system exit on failure to match symmetry structure, dimensions, or element values. Used in input files in the test suite. """ if (expected.nirrep() != computed.nirrep()): print("\t%s has %d irreps, but %s has %d\n." % (expected.name(), expected.nirrep(), computed.name(), computed.nirrep())) sys.exit(1) if (expected.symmetry() != computed.symmetry()): print("\t%s has %d symmetry, but %s has %d\n." % (expected.name(), expected.symmetry(), computed.name(), computed.symmetry())) sys.exit(1) nirreps = expected.nirrep() symmetry = expected.symmetry() for irrep in range(nirreps): if(expected.rows(irrep) != computed.rows(irrep)): print("\t%s has %d rows in irrep %d, but %s has %d\n." % (expected.name(), expected.rows(irrep), irrep, computed.name(), computed.rows(irrep))) sys.exit(1) if(expected.cols(irrep ^ symmetry) != computed.cols(irrep ^ symmetry)): print("\t%s has %d columns in irrep, but %s has %d\n." % (expected.name(), expected.cols(irrep), irrep, computed.name(), computed.cols(irrep))) sys.exit(1) rows = expected.rows(irrep) cols = expected.cols(irrep ^ symmetry) failed = 0 for row in range(rows): for col in range(cols): if(abs(expected.get(irrep, row, col) - computed.get(irrep, row, col)) > 10 ** (-digits)): print("\t%s: computed value (%s) does not match (%s)." % (label, expected.get(irrep, row, col), computed.get(irrep, row, col))) failed = 1 break if(failed): print("Check your output file for reporting of the matrices.") psi4.print_out("The Failed Test Matrices\n") psi4.print_out("Computed Matrix (2nd matrix passed in)\n") computed.print_out() psi4.print_out("Expected Matrix (1st matrix passed in)\n") expected.print_out() sys.exit(1) success(label)
def writeCSX(name, **kwargs): """function to write the CSX file """ if not psi4.get_global_option('WRITE_CSX'): return # import csx_api for csx writing import os import math import inspect #import openbabel import qcdb import qcdb.periodictable import csx2_api as api lowername = name.lower() # Make sure the molecule the user provided is the active one if ('molecule' in kwargs): activate(kwargs['molecule']) del kwargs['molecule'] molecule = psi4.get_active_molecule() molecule.update_geometry() # Determine the derivative type calledby = inspect.stack()[1][3] derdict = { 'energy': 0, 'property': 0, 'gradient': 1, 'optimize': 1, 'frequency': 2, 'frequencies': 2, 'hessian': 2, } dertype = derdict[calledby] hasFreq = False # Start to write the CSX file # First grab molecular information and energies from psi4 geom = molecule.save_string_xyz() # OB atomLine = geom.split('\n') # OB # general molecular information atomNum = molecule.natom() molSym = molecule.schoenflies_symbol() molCharge = molecule.molecular_charge() molMulti = molecule.multiplicity() # energy information molBasis = psi4.get_global_option('BASIS') molSpin = psi4.get_global_option('REFERENCE') molMethod = psi4.get_global_option('WFN') mol1E = psi4.get_variable('ONE-ELECTRON ENERGY') mol2E = psi4.get_variable('TWO-ELECTRON ENERGY') molNE = psi4.get_variable('NUCLEAR REPULSION ENERGY') molPE = mol1E + mol2E molEE = psi4.get_variable('CURRENT ENERGY') # wavefunction information try: wfn = kwargs['wfn'] except AttributeError: pass if wfn: molOrbE = wfn.epsilon_a() molOrbEb = wfn.epsilon_b() orbNmopi = wfn.nmopi() orbNsopi = wfn.nsopi() orbNum = wfn.nmo() if molOrbE else 0 orbSNum = wfn.nso() molOrb = wfn.Ca() orbNirrep = wfn.nirrep() orbAotoso = wfn.aotoso() orbDoccpi = wfn.doccpi() orbSoccpi = wfn.soccpi() basisNbf = wfn.basisset().nbf() basisDim = psi4.Dimension(1, 'basisDim') basisDim.__setitem__(0, basisNbf) wfnRestricted = True orbE = [] hlist = [] orblist = [] orbOcc = [] molOrbmo = psi4.Matrix('molOrbmo', basisDim, orbNmopi) molOrbmo.gemm(False, False, 1.0, orbAotoso, molOrb, 0.0) if molSpin == 'UHF': wfnRestricted = False orbEb = [] hlistCb = [] orblistCb = [] orbOccCb = [] molOrbCb = wfn.Cb() molOrbmoCb = psi4.Matrix('molOrbmoCb', basisDim, orbNmopi) molOrbmoCb.gemm(False, False, 1.0, orbAotoso, molOrbCb, 0.0) count = 0 eleExtra = 1 if wfnRestricted else 0 for ih in range(orbNirrep): for iorb in range(orbNmopi.__getitem__(ih)): hlist.append(ih) orblist.append(iorb) if molOrbE: orbE.append(molOrbE.get(count)) eleNum = 1 if iorb < (orbDoccpi.__getitem__(ih) + orbSoccpi.__getitem__(ih)) else 0 eleNum += eleExtra if iorb < orbDoccpi.__getitem__(ih) else 0 orbOcc.append(eleNum) count += 1 orbMos = sorted(zip(orbE, zip(hlist, orblist))) orbOccString = ' '.join(str(x) for x in sorted(orbOcc, reverse=True)) orbCaString = [] for imos in range(orbNum): (h, s) = orbMos[imos][1] orbCa = [] for iso in range(orbSNum): orbEle = molOrbmo.get(h, iso, s) orbCa.append(orbEle) orbCaString.append(' '.join(str(x) for x in orbCa)) orbEString = ' '.join(str(x) for x in sorted(orbE)) # now for beta spin if not wfnRestricted: count = 0 for ih in range(orbNirrep): for iorb in range(orbNmopi.__getitem__(ih)): hlistCb.append(ih) orblist.append(iorb) if molOrbEb: orbEb.append(molOrbEb.get(count)) eleNum = 1 if iorb < (orbDoccpi.__getitem__(ih) + orbSoccpi.__getitem__(ih)) else 0 if iorb < orbDoccpi.__getitem__(ih): eleNum += eleExtra orbOccCb.append(eleNum) count += 1 orbMosCb = sorted(zip(orbEb, zip(hlist, orblist))) orbOccCbString = ' '.join(str(x) for x in sorted(orbOccCb, reverse=True)) orbCbString = [] for imos in range(orbNum): (h, s) = orbMosCb[imos][1] orbCb = [] for iso in range(orbSNum): orbEle = molOrbmoCb.get(h, iso, s) orbCb.append(orbEle) orbCbString.append(' '.join(str(x) for x in orbCb)) orbEbString = ' '.join(str(x) for x in sorted(orbEb)) # orbColString = ' '.join(str(x) for x in orbCol) if wfnRestricted: wfn1 = api.waveFunctionType( orbitalCount=orbNum, orbitalOccupancies=orbOccString) orbe1 = api.stringArrayType(unit='gc:hartree') orbe1.set_valueOf_(orbEString) orbs1 = api.orbitalsType() for iorb in range(orbNum): orb1 = api.stringArrayType(id=iorb+1) orb1.set_valueOf_(orbCaString[iorb]) orbs1.add_orbital(orb1) wfn1.set_orbitals(orbs1) wfn1.set_orbitalEnergies(orbe1) else: wfn1 = api.waveFunctionType(orbitalCount=orbNum) # alpha electron: 1.5 orbe1 = api.stringArrayType(unit='gc:hartree') orbe1.set_valueOf_(orbEString) wfn1.set_alphaOrbitalEnergies(orbe1) wfn1.set_alphaOrbitalOccupancies(orbOccString) aorbs1 = api.orbitalsType() for iorb in range(orbNum): orb1 = api.stringArrayType(id=iorb+1) orb1.set_valueOf_(orbCaString[iorb]) aorbs1.add_orbital(orb1) wfn1.set_alphaOrbitals(aorbs1) # beta electron: 1.5 orbeb1 = api.stringArrayType(unit='gc:hartree') orbeb1.set_valueOf_(orbEbString) wfn1.set_betaOrbitalEnergies(orbeb1) wfn1.set_betaOrbitalOccupancies(orbOccCbString) borbs1 = api.orbitalsType() for iorb in range(orbNum): orb1 = api.stringArrayType(id=iorb+1) orb1.set_valueOf_(orbCbString[iorb]) borbs1.add_orbital(orb1) wfn1.set_betaOrbitals(borbs1) # frequency information if dertype == 2: hasFreq = True molFreq = psi4.get_frequencies() molFreqNum = molFreq.dim(0) frq = [] irInt = [] for ifrq in range(molFreqNum): frq.append(molFreq.get(ifrq)) irInt.append(0.0) frqString = ' '.join(str(x) for x in frq) intString = ' '.join(str(x) for x in irInt) normMod = psi4.get_normalmodes() normMdString = [] count = 0 for ifrq in range(molFreqNum): normM = [] for iatm in range(atomNum): for ixyz in range(3): normM.append(normMod.get(count)) count += 1 normMdString.append(' '.join(str(x) for x in normM)) vib1 = api.vibAnalysisType(vibrationCount=molFreqNum) freq1 = api.stringArrayType(unit="gc:cm-1") freq1.set_valueOf_(frqString) vib1.set_frequencies(freq1) irint1 = api.stringArrayType() irint1.set_valueOf_(intString) vib1.set_irIntensities(irint1) norms1 = api.normalModesType() for ifrq in range(molFreqNum): norm1 = api.normalModeType(id=ifrq+1) norm1.set_valueOf_(normMdString[ifrq]) norms1.add_normalMode(norm1) vib1.set_normalModes(norms1) # dipole moment information molDipoleX = psi4.get_variable('CURRENT DIPOLE X') molDipoleY = psi4.get_variable('CURRENT DIPOLE Y') molDipoleZ = psi4.get_variable('CURRENT DIPOLE Z') molDipoleTot = math.sqrt( molDipoleX * molDipoleX + molDipoleY * molDipoleY + molDipoleZ * molDipoleZ) prop1 = api.propertiesType() sprop1 = api.propertyType( name='dipoleMomentX', unit='gc:debye') sprop1.set_valueOf_(molDipoleX) sprop2 = api.propertyType( name='dipoleMomentY', unit='gc:debye') sprop2.set_valueOf_(molDipoleY) sprop3 = api.propertyType( name='dipoleMomentZ', unit='gc:debye') sprop3.set_valueOf_(molDipoleZ) sprop4 = api.propertyType( name='dipoleMomentAverage', unit='gc:debye') sprop4.set_valueOf_(molDipoleTot) prop1.add_systemProperty(sprop1) prop1.add_systemProperty(sprop2) prop1.add_systemProperty(sprop3) prop1.add_systemProperty(sprop4) # get the basename for the CSX file psio = psi4.IO.shared_object() namespace = psio.get_default_namespace() #csxfilename = '.'.join([namespace, str(os.getpid()), 'csx']) csxfilename = os.path.splitext(psi4.outfile_name())[0] + '.csx' csxfile = open(csxfilename, 'w') csxVer = psi4.get_global_option('CSX_VERSION') # Both CSX versions 0 and 1 depended on the procedures table, which in # turn required the writeCSX function to be in the driver.py file # itself. Starting with 1.5 (1, to run), this dependence is broken and # CSX has been shifted into a plugin. # Start to generate CSX elements # CSX version 1.5 if csxVer == 2.0: # import csx1_api as api cs1 = api.csType(version='2.0') #5') # molPublication section: 1.5 mp1 = api.mpubType( title=psi4.get_global_option('PUBLICATIONTITLE'), abstract=psi4.get_global_option('PUBLICATIONABSTRACT'), publisher=psi4.get_global_option('PUBLICATIONPUBLISHER'), status=['PRELIMINARY', 'DRAFT', 'FINAL'].index(psi4.get_global_option('PUBLICATIONSTATUS')), category=psi4.get_global_option('PUBLICATIONCATEGORY'), visibility=['PRIVATE', 'PROTECTED', 'PUBLIC'].index(psi4.get_global_option('PUBLICATIONVISIBILITY')), tags=psi4.get_global_option('PUBLICATIONTAGS'), key=psi4.get_global_option('PUBLICATIONKEY')) email = psi4.get_global_option('EMAIL').replace('__', '@') mp1.add_author(api.authorType( creator=psi4.get_global_option('CORRESPONDINGAUTHOR'), type_='gc:CorrespondingAuthor', organization=psi4.get_global_option('ORGANIZATION'), email=None if email == '' else email)) #mp1 = api.mpType( # title='', abstract='', publisher='', status=0, category=2, visibility=0, tags='', key='') mp1.set_sourcePackage(api.sourcePackageType(name='Psi4', version=psi4.version())) #mp1.add_author(api.authorType(creator='', type_='cs:corresponding', organization='', email='')) cs1.set_molecularPublication(mp1) # molSystem section: 1.5 ms1 = api.msysType( systemCharge=molCharge, systemMultiplicity=molMulti, id='s1') temp1 = api.dataWithUnitsType(unit='gc:kelvin') temp1.set_valueOf_(0.0) # LAB dispute ms1.set_systemTemperature(temp1) mol1 = api.moleculeType(id='m1', atomCount=molecule.natom()) #OBmol1 = api.moleculeType(id='m1', atomCount=atomNum) #OBobmol1 = openbabel.OBMol() #OBfor iatm in range(atomNum): #OB atomField = atomLine[iatm + 1].split() #OB atmSymbol = atomField[0] #OB xCoord = float(atomField[1]) #OB yCoord = float(atomField[2]) #OB zCoord = float(atomField[3]) #OB obatm = obmol1.NewAtom() #OB obatm.SetAtomicNum(qcdb.periodictable.el2z[atmSymbol.upper()]) #OB obatm.SetVector(xCoord, yCoord, zCoord) #OBobmol1.ConnectTheDots() #OBobmol1.PerceiveBondOrders() #OBobmol1.SetTotalSpinMultiplicity(molMulti) #OBobmol1.SetTotalCharge(molCharge) #OBconv1 = openbabel.OBConversion() #OBconv1.SetInAndOutFormats('mol', 'inchi') #OBconv1.SetOptions('K', conv1.OUTOPTIONS) #OBinchikey = conv1.WriteString(obmol1) #OBmol1.set_inchiKey(inchikey.rstrip()) #OBiatm = 0 for at in range(molecule.natom()): #xCoord1 = api.dataWithUnitsType(unit='cs:angstrom') #yCoord1 = api.dataWithUnitsType(unit='cs:angstrom') #zCoord1 = api.dataWithUnitsType(unit='cs:angstrom') #xCoord1.set_valueOf_(molecule.x(at) * p4const.psi_bohr2angstroms) #yCoord1.set_valueOf_(molecule.y(at) * p4const.psi_bohr2angstroms) #zCoord1.set_valueOf_(molecule.z(at) * p4const.psi_bohr2angstroms) xCoord1 = api.dataWithUnitsType(unit='gc:bohr') yCoord1 = api.dataWithUnitsType(unit='gc:bohr') zCoord1 = api.dataWithUnitsType(unit='gc:bohr') xCoord1.set_valueOf_(molecule.x(at)) yCoord1.set_valueOf_(molecule.y(at)) zCoord1.set_valueOf_(molecule.z(at)) # LAB 8jun2015: not getting masses from OB anymore so now dependent on qc programs # current proposition is changing API so masses only go into CSX if relevant (e.g., vib) # same situation as temperature atm = api.atomType( id='a' + str(at + 1), elementSymbol=molecule.symbol(at), atomMass=molecule.mass(at), # psi4 uses mass of most common isotope; OB uses natural distribution mass xCoord3D=xCoord1, yCoord3D=yCoord1, zCoord3D=zCoord1, basisSet='bse:' + molBasis, calculatedAtomCharge=0, formalAtomCharge=0) #OBiatm += 1 #OBcoord1 = api.coordinationType() #OBibond = 0 #OBfor nb_atom in openbabel.OBAtomAtomIter(obatom): #OB bond = obatom.GetBond(nb_atom) #OB bond1 = api.bondType( #OB id1='a' + str(obatom.GetId() + 1), #OB id2='a' + str(nb_atom.GetId() + 1)) #OB if bond.GetBondOrder() == 1: #OB bond1.set_valueOf_('single') #OB elif bond.GetBondOrder() == 2: #OB bond1.set_valueOf_('double') #OB elif bond.GetBondOrder() == 3: #OB bond1.set_valueOf_('triple') #OB elif bond.GetBondOrder() == 5: #OB bond1.set_valueOf_('aromatic') #OB else: #OB print('wrong bond order') #OB coord1.add_bond(bond1) #OB ibond += 1 #OBcoord1.set_bondCount(ibond) #OBatm.set_coordination(coord1) mol1.add_atom(atm) ms1.add_molecule(mol1) cs1.set_molecularSystem(ms1) # molCalculation section: 1.5 mc1 = api.mcalType(id='c1') qm1 = api.qmCalcType() srs1 = api.srsMethodType() psivars = psi4.get_variables() def form_ene(mandatoryPsivars, optionalPsivars={}, excessPsivars={}): """ """ ene = api.energiesType(unit='gc:hartree') for pv, csx in mandatoryPsivars.iteritems(): term = api.energyType(type_=csx) term.set_valueOf_(psivars.pop(pv)) ene.add_energy(term) for pv, csx in optionalPsivars.iteritems(): if pv in psivars: term = api.energyType(type_=csx) term.set_valueOf_(psivars.pop(pv)) ene.add_energy(term) for pv in excessPsivars: if pv in psivars: psivars.pop(pv) return ene # Reference stage- every calc has one if 'CCSD TOTAL ENERGY' in psivars or 'CCSD(T) TOTAL ENERGY' in psivars \ or 'CISD TOTAL ENERGY' in psivars or 'FCI TOTAL ENERGY' in psivars \ or 'QCISD TOTAL ENERGY' in psivars or 'QCISD(T) TOTAL ENERGY' in psivars: mdm1 = api.srsmdMethodType() # CCSD(T): 1.5 if 'CCSD(T) TOTAL ENERGY' in psivars: mandatoryPsivars = { 'CCSD(T) CORRELATION ENERGY': 'gc:correlation', 'CCSD(T) TOTAL ENERGY': 'gc:electronic'} if not all([pv in psivars for pv in mandatoryPsivars.keys()]): raise CSXError("""Malformed CCSD(T) computation""") block = api.resultType( # TODO should be pointing to HF for correlation, maybe to MP2 for guess methodology='gc:normal', # TODO handle dfcc spinType='gc:' + molSpin, # TODO could have a closed-shell corl mtd atop open-shell scf? basisSet='bse:' + molBasis) block.set_energies(form_ene(mandatoryPsivars)) if hasFreq: block.set_vibrationalAnalysis(vib1) mdm1.set_ccsd_t(block) # CCSD: 1.5 elif 'CCSD TOTAL ENERGY' in psivars: mandatoryPsivars = { 'CCSD CORRELATION ENERGY': 'gc:correlation', 'CCSD TOTAL ENERGY': 'gc:electronic'} if not all([pv in psivars for pv in mandatoryPsivars.keys()]): raise CSXError("""Malformed CCSD computation""") block = api.resultType( # TODO should be pointing to HF for correlation, maybe to MP2 for guess methodology='gc:normal', # TODO handle dfcc spinType='gc:' + molSpin, # TODO could have a closed-shell corl mtd atop open-shell scf? basisSet='bse:' + molBasis) block.set_energies(form_ene(mandatoryPsivars)) if hasFreq: block.set_vibrationalAnalysis(vib1) mdm1.set_ccsd(block) # CISD: 1.5 elif 'CISD TOTAL ENERGY' in psivars: mandatoryPsivars = { 'CISD CORRELATION ENERGY': 'gc:correlation', 'CISD TOTAL ENERGY': 'gc:electronic'} if not all([pv in psivars for pv in mandatoryPsivars.keys()]): raise CSXError("""Malformed CISD computation""") block = api.resultType( # TODO should be pointing to HF for correlation, maybe to MP2 for guess methodology='gc:normal', # TODO handle dfcc spinType='gc:' + molSpin, # TODO could have a closed-shell corl mtd atop open-shell scf? basisSet='bse:' + molBasis) block.set_energies(form_ene(mandatoryPsivars)) if hasFreq: block.set_vibrationalAnalysis(vib1) mdm1.set_cisd(block) # FCI: 1.5 elif 'FCI TOTAL ENERGY' in psivars: mandatoryPsivars = { 'FCI CORRELATION ENERGY': 'gc:correlation', 'FCI TOTAL ENERGY': 'gc:electronic'} if not all([pv in psivars for pv in mandatoryPsivars.keys()]): raise CSXError("""Malformed FCI computation""") block = api.resultType( # TODO should be pointing to HF for correlation, maybe to MP2 for guess methodology='gc:normal', # TODO handle dfcc spinType='gc:' + molSpin, # TODO could have a closed-shell corl mtd atop open-shell scf? basisSet='bse:' + molBasis) block.set_energies(form_ene(mandatoryPsivars)) mdm1.set_fci(block) # QCISD(T): 1.5 elif 'QCISD(T) TOTAL ENERGY' in psivars: mandatoryPsivars = { 'QCISD(T) CORRELATION ENERGY': 'gc:correlation', 'QCISD(T) TOTAL ENERGY': 'gc:electronic'} if not all([pv in psivars for pv in mandatoryPsivars.keys()]): raise CSXError("""Malformed QCISD(T) computation""") block = api.resultType( # TODO should be pointing to HF for correlation, maybe to MP2 for guess methodology='gc:normal', # TODO handle dfcc spinType='gc:' + molSpin, # TODO could have a closed-shell corl mtd atop open-shell scf? basisSet='bse:' + molBasis) block.set_energies(form_ene(mandatoryPsivars)) if hasFreq: block.set_vibrationalAnalysis(vib1) mdm1.set_qcisd_t(block) # QCISD: 1.5 elif 'QCISD TOTAL ENERGY' in psivars: mandatoryPsivars = { 'QCISD CORRELATION ENERGY': 'gc:correlation', 'QCISD TOTAL ENERGY': 'gc:electronic'} if not all([pv in psivars for pv in mandatoryPsivars.keys()]): raise CSXError("""Malformed QCISD computation""") block = api.resultType( # TODO should be pointing to HF for correlation, maybe to MP2 for guess methodology='gc:normal', # TODO handle dfcc spinType='gc:' + molSpin, # TODO could have a closed-shell corl mtd atop open-shell scf? basisSet='bse:' + molBasis) block.set_energies(form_ene(mandatoryPsivars)) if hasFreq: block.set_vibrationalAnalysis(vib1) mdm1.set_qcisd(block) srs1.set_multipleDeterminant(mdm1) elif 'DFT TOTAL ENERGY' in psivars or 'HF TOTAL ENERGY' in psivars \ or 'MP2 TOTAL ENERGY' in psivars or 'MP3 TOTAL ENERGY' in psivars \ or 'MP4 TOTAL ENERGY' in psivars: sdm1 = api.srssdMethodType() # DFT 1.5 if 'DFT TOTAL ENERGY' in psivars: # TODO robust enough to avoid MP2C, etc.? mandatoryPsivars = { 'NUCLEAR REPULSION ENERGY': 'gc:nuclearRepulsion', 'DFT FUNCTIONAL TOTAL ENERGY': 'gc:dftFunctional', 'DFT TOTAL ENERGY': 'gc:electronic'} optionalPsivars = { 'DOUBLE-HYBRID CORRECTION ENERGY': 'gc:doubleHybrid correction', 'DISPERSION CORRECTION ENERGY': 'gc:dispersion correction'} excessPsivars = [ 'MP2 TOTAL ENERGY', 'MP2 CORRELATION ENERGY', 'MP2 SAME-SPIN CORRELATION ENERGY'] if not all([pv in psivars for pv in mandatoryPsivars.keys()]): raise CSXError("""Malformed DFT computation""") block = api.resultType( methodology='gc:normal', # TODO handle dfhf, dfmp spinType='gc:' + molSpin, basisSet='bse:' + molBasis, dftFunctional=name) # TODO this'll need to be exported block.set_energies(form_ene(mandatoryPsivars, optionalPsivars, excessPsivars)) if wfn: block.set_waveFunction(wfn1) if hasFreq: block.set_vibrationalAnalysis(vib1) block.set_properties(prop1) sdm1.set_dft(block) # post-reference block # MP4: 1.5 elif 'MP4 TOTAL ENERGY' in psivars: mandatoryPsivars = { 'MP4 CORRELATION ENERGY': 'gc:correlation', 'MP4 TOTAL ENERGY': 'gc:electronic'} optionalPsivars = { 'MP4 SAME-SPIN CORRELATION ENERGY': 'gc:sameSpin correlation'} if not all([pv in psivars for pv in mandatoryPsivars.keys()]): raise CSXError("""Malformed MP4 computation""") block = api.resultType( # TODO should be pointing to HF for correlation methodology='gc:normal', # TODO handle dfmp spinType='gc:' + molSpin, # TODO could have a closed-shell corl mtd atop open-shell scf? basisSet='bse:' + molBasis) block.set_energies(form_ene(mandatoryPsivars, optionalPsivars)) if wfn: block.set_waveFunction(wfn1) if hasFreq: block.set_vibrationalAnalysis(vib1) block.set_properties(prop1) sdm1.set_mp4(block) # MP3: 1.5 elif 'MP3 TOTAL ENERGY' in psivars: mandatoryPsivars = { 'MP3 CORRELATION ENERGY': 'gc:correlation', 'MP3 TOTAL ENERGY': 'gc:electronic'} optionalPsivars = { 'MP3 SAME-SPIN CORRELATION ENERGY': 'gc:sameSpin correlation'} if not all([pv in psivars for pv in mandatoryPsivars.keys()]): raise CSXError("""Malformed MP3 computation""") block = api.resultType( # TODO should be pointing to HF for correlation methodology='gc:normal', # TODO handle dfmp spinType='gc:' + molSpin, # TODO could have a closed-shell corl mtd atop open-shell scf? basisSet='bse:' + molBasis) block.set_energies(form_ene(mandatoryPsivars, optionalPsivars)) if wfn: block.set_waveFunction(wfn1) if hasFreq: block.set_vibrationalAnalysis(vib1) block.set_properties(prop1) sdm1.set_mp3(block) # MP2: 1.5 elif 'MP2 TOTAL ENERGY' in psivars: mandatoryPsivars = { 'MP2 CORRELATION ENERGY': 'gc:correlation', 'MP2 TOTAL ENERGY': 'gc:electronic'} optionalPsivars = { 'MP2 SAME-SPIN CORRELATION ENERGY': 'gc:sameSpin correlation'} if not all([pv in psivars for pv in mandatoryPsivars.keys()]): raise CSXError("""Malformed MP2 computation""") block = api.resultType( # TODO should be pointing to HF for correlation methodology='gc:normal', # TODO handle dfmp spinType='gc:' + molSpin, # TODO could have a closed-shell corl mtd atop open-shell scf? basisSet='bse:' + molBasis) block.set_energies(form_ene(mandatoryPsivars, optionalPsivars)) if wfn: block.set_waveFunction(wfn1) if hasFreq: block.set_vibrationalAnalysis(vib1) block.set_properties(prop1) sdm1.set_mp2(block) # SCF: 1.5 elif 'HF TOTAL ENERGY' in psivars: mandatoryPsivars = { 'NUCLEAR REPULSION ENERGY': 'gc:nuclearRepulsion', 'HF TOTAL ENERGY': 'gc:electronic'} if not all([pv in psivars for pv in mandatoryPsivars.keys()]): raise CSXError("""Malformed HF computation""") block = api.resultType( methodology='gc:normal', # TODO handle dfhf, dfmp spinType='gc:' + molSpin, basisSet='bse:' + molBasis) block.set_energies(form_ene(mandatoryPsivars)) if wfn: block.set_waveFunction(wfn1) if hasFreq: block.set_vibrationalAnalysis(vib1) block.set_properties(prop1) sdm1.set_abinitioScf(block) else: psi4.print_out("""\nCSX version {0} does not support """ """method {1} for {2}\n""".format( csxVer, lowername, 'energies')) srs1.set_singleDeterminant(sdm1) #print('CSX not harvesting: ', ', '.join(psivars)) qm1.set_singleReferenceState(srs1) mc1.set_quantumMechanics(qm1) cs1.set_molecularCalculation(mc1) else: print('The future CSX file is here') csxfile.write('<?xml version="1.0" encoding="UTF-8"?>\n') cs1.export(csxfile, 0) csxfile.close()
def auto_fragments(**kwargs): r"""Detects fragments if the user does not supply them. Currently only used for the WebMO implementation of SAPT. :returns: :ref:`Molecule<sec:psimod_Molecule>`) |w--w| fragmented molecule. :type molecule: :ref:`molecule <op_py_molecule>` :param molecule: ``h2o`` || etc. The target molecule, if not the last molecule defined. :examples: >>> # [1] replicates with cbs() the simple model chemistry scf/cc-pVDZ: set basis cc-pVDZ energy('scf') >>> molecule mol {\nH 0.0 0.0 0.0\nH 2.0 0.0 0.0\nF 0.0 1.0 0.0\nF 2.0 1.0 0.0\n} >>> print mol.nfragments() # 1 >>> fragmol = auto_fragments() >>> print fragmol.nfragments() # 2 """ # Make sure the molecule the user provided is the active one molecule = kwargs.pop('molecule', psi4.get_active_molecule()) molecule.update_geometry() molname = molecule.name() geom = molecule.save_string_xyz() numatoms = molecule.natom() VdW = [1.2, 1.7, 1.5, 1.55, 1.52, 1.9, 1.85, 1.8] symbol = list(range(numatoms)) X = [0.0] * numatoms Y = [0.0] * numatoms Z = [0.0] * numatoms Queue = [] White = [] Black = [] F = geom.split('\n') for f in range(numatoms): A = F[f + 1].split() symbol[f] = A[0] X[f] = float(A[1]) Y[f] = float(A[2]) Z[f] = float(A[3]) White.append(f) Fragment = [[] for i in range(numatoms)] # stores fragments start = 0 # starts with the first atom in the list Queue.append(start) White.remove(start) frag = 0 while((len(White) > 0) or (len(Queue) > 0)): # Iterates to the next fragment while(len(Queue) > 0): # BFS within a fragment for u in Queue: # find all nearest Neighbors # (still coloured white) to vertex u for i in White: Distance = math.sqrt((X[i] - X[u]) * (X[i] - X[u]) + (Y[i] - Y[u]) * (Y[i] - Y[u]) + (Z[i] - Z[u]) * (Z[i] - Z[u])) if Distance < _autofragment_convert(u, symbol) + _autofragment_convert(i, symbol): Queue.append(i) # if you find you, put it in the que White.remove(i) # and remove it from the untouched list Queue.remove(u) # remove focus from Queue Black.append(u) Fragment[frag].append(int(u + 1)) # add to group (adding 1 to start # list at one instead of zero) if(len(White) != 0): # cant move White->Queue if no more exist Queue.append(White[0]) White.remove(White[0]) frag += 1 new_geom = """\n""" for i in Fragment[0]: new_geom = new_geom + F[i].lstrip() + """\n""" new_geom = new_geom + """--\n""" for j in Fragment[1]: new_geom = new_geom + F[j].lstrip() + """\n""" new_geom = new_geom + """units angstrom\n""" moleculenew = psi4.Molecule.create_molecule_from_string(new_geom) moleculenew.set_name(molname) moleculenew.update_geometry() moleculenew.print_cluster() psi4.print_out(""" Exiting auto_fragments\n""") return moleculenew
def __init__(self, msg): PsiException.__init__(self, msg) self.message = msg psi4.print_out('\nCSXException: %s\n\n' % (msg))
def run_roa(name, **kwargs): # Get list of omega values -> Make sure we only have one wavelength # Catch this now before any real work gets done omega = psi4.get_option('CCRESPONSE','OMEGA') if len(omega) > 2: raise Exception('ROA scattering can only be performed for one wavelength.') else: pass psi4.print_out('Running ROA computation. Subdirectories for each ' 'required displaced geometry have been created.\n\n') ### Initialize database db = shelve.open('database',writeback=True) if 'inputs_generated' not in db: initialize_database(db) ### Generate input files if not db['inputs_generated']: generate_inputs(name, db) db['inputs_generated'] = True ### If 'serial' calculation, proceed with subdir execution ### Check job status if db['inputs_generated'] and not db['jobs_complete']: print('Checking status') roa_stat(db) for job,status in db['job_status'].items(): print("{} --> {}".format(job,status)) ### Compute ROA Scattering if db['jobs_complete']: # SAVE this for when multiple wavelengths works # # Get list of omega values # omega = psi4.get_option('CCRESPONSE','OMEGA') # if len(omega) > 1: # units = copy.copy(omega[-1]) # omega.pop() # else: # units = 'atomic' # wavelength = copy.copy(omega[0]) # # Set up units for scatter.cc # if units == 'NM': # wavelength = (psi_c * psi_h * 1*(10**-9))/(wavelength * psi_hartree2J) # if units == 'HZ': # wavelength = wavelength * psi_h / psi_hartree2J # if units == 'EV': # wavelength = wavelength / psi_hartree2ev # if units == 'atomic': # pass # Initialize tensor lists dip_polar_list = [] opt_rot_list = [] dip_quad_polar_list = [] gauge_list = [] make_gauge_list(gauge_list) # Gather data synthesize_dipole_polar(db,dip_polar_list) synthesize_opt_rot(db,opt_rot_list) synthesize_dip_quad_polar(db,dip_quad_polar_list) # Compute Scattering # Run new function (src/bin/ccresponse/scatter.cc) psi4.print_out('Running scatter function') step = psi4.get_local_option('FINDIF','DISP_SIZE') for gauge in opt_rot_list: g_idx = opt_rot_list.index(gauge) # print('\n\n----------------------------------------------------------------------') # print('\t%%%%%%%%%% {} %%%%%%%%%%'.format(gauge_list[g_idx])) # print('----------------------------------------------------------------------\n\n') psi4.print_out('\n\n----------------------------------------------------------------------\n') psi4.print_out('\t%%%%%%%%%% {} %%%%%%%%%%\n'.format(gauge_list[g_idx])) psi4.print_out('----------------------------------------------------------------------\n\n') print('roa.py:85 I am not being passed a molecule, grabbing from global :(') psi4.scatter(psi4.get_active_molecule(), step, dip_polar_list, gauge, dip_quad_polar_list) db.close()
def process_input(raw_input, print_level=1): """Function to preprocess *raw input*, the text of the input file, then parse it, validate it for format, and convert it into legitimate Python. *raw_input* is printed to the output file unless *print_level* =0. Does a series of regular expression filters, where the matching portion of the input is replaced by the output of the corresponding function (in this module) call. Returns a string concatenating module import lines, a copy of the user's .psi4rc files, a setting of the scratch directory, a dummy molecule, and the processed *raw_input*. """ # Check if the infile is actually an outfile (yeah we did) psi4_id = re.compile(r'PSI4: An Open-Source Ab Initio Electronic Structure Package') if (re.search(psi4_id, raw_input)): input_lines = raw_input.split("\n") input_re = re.compile(r'^\s*?\=\=> Input File <\=\=') input_start = -1 for line_count in range(len(input_lines)): line = input_lines[line_count] if re.match(input_re, line): input_start = line_count + 3 break stop_re = re.compile(r'^-{74}') input_stop = -1 for line_count in range(input_start, len(input_lines)): line = input_lines[line_count] if re.match(stop_re, line): input_stop = line_count break if (input_start == -1 or input_stop == -1): print('Cannot extract infile from outfile.') sys.exit(1) raw_input = '\n'.join(input_lines[input_start:input_stop]) raw_input += '\n' # Echo the infile on the outfile if print_level > 0: psi4.print_out("\n ==> Input File <==\n\n") psi4.print_out("--------------------------------------------------------------------------\n") psi4.print_out(raw_input) psi4.print_out("--------------------------------------------------------------------------\n") psi4.flush_outfile() #NOTE: If adding mulitline data to the preprocessor, use ONLY the following syntax: # function [objname] { ... } # which has the regex capture group: # # r'^(\s*?)FUNCTION\s*(\w*?)\s*\{(.*?)\}', re.MULTILINE | re.DOTALL | re.IGNORECASE # # your function is in capture group #1 # your objname is in capture group #2 # your data is in capture group #3 # Nuke all comments comment = re.compile(r'[^\\]#.*') temp = re.sub(comment, '', raw_input) # Now, nuke any escapes from comment lines comment = re.compile(r'\\#') temp = re.sub(comment, '#', temp) # Check the brackets and parentheses match up, as long as this is not a pickle input file if not re.search(r'pickle_kw', temp): check_parentheses_and_brackets(temp, 1) # First, remove everything from lines containing only spaces blankline = re.compile(r'^\s*$') temp = re.sub(blankline, '', temp, re.MULTILINE) # Look for things like # set matrix [ # [ 1, 2 ], # [ 3, 4 ] # ] # and put them on a single line temp = process_multiline_arrays(temp) # Process all "set name? { ... }" set_commands = re.compile(r'^(\s*?)set\s*([-,\w]*?)[\s=]*\{(.*?)\}', re.MULTILINE | re.DOTALL | re.IGNORECASE) temp = re.sub(set_commands, process_set_commands, temp) # Process all individual "set (module_list) key {[value_list] or $value or value}" # N.B. We have to be careful here, because \s matches \n, leading to potential problems # with undesired multiline matches. Better the double-negative [^\S\n] instead, which # will match any space, tab, etc., except a newline set_command = re.compile(r'^(\s*?)set\s+(?:([-,\w]+)[^\S\n]+)?(\w+)(?:[^\S\n]|=)+((\[.*\])|(\$?[-+,*()\.\w]+))\s*$', re.MULTILINE | re.IGNORECASE) temp = re.sub(set_command, process_set_command, temp) # Process "molecule name? { ... }" molecule = re.compile(r'^(\s*?)molecule[=\s]*(\w*?)\s*\{(.*?)\}', re.MULTILINE | re.DOTALL | re.IGNORECASE) temp = re.sub(molecule, process_molecule_command, temp) # Process "external name? { ... }" external = re.compile(r'^(\s*?)external[=\s]*(\w*?)\s*\{(.*?)\}', re.MULTILINE | re.DOTALL | re.IGNORECASE) temp = re.sub(external, process_external_command, temp) # Process "pcm name? { ... }" pcm = re.compile(r'^(\s*?)pcm[=\s]*(\w*?)\s*\{(.*?)^\}', re.MULTILINE | re.DOTALL | re.IGNORECASE) temp = re.sub(pcm, process_pcm_command, temp) # Then remove repeated newlines multiplenewlines = re.compile(r'\n+') temp = re.sub(multiplenewlines, '\n', temp) # Process " extract" extract = re.compile(r'(\s*?)(\w+)\s*=\s*\w+\.extract_subsets.*', re.IGNORECASE) temp = re.sub(extract, process_extract_command, temp) # Process "print" and transform it to "psi4.print_out()" #print_string = re.compile(r'(\s*?)print\s+(.*)', re.IGNORECASE) #temp = re.sub(print_string, process_print_command, temp) # Process "memory ... " memory_string = re.compile(r'(\s*?)memory\s+([+-]?\d*\.?\d+)\s+([KMG]i?B)', re.IGNORECASE) temp = re.sub(memory_string, process_memory_command, temp) # Process "basis file ... " basis_file = re.compile(r'(\s*?)basis\s+file\s*(\b.*\b)\s*$', re.MULTILINE | re.IGNORECASE) temp = re.sub(basis_file, process_basis_file, temp) # Process "basis name { ... }" basis_block = re.compile(r'(\s*?)basis[=\s]*\{(.*?)\}', re.MULTILINE | re.DOTALL | re.IGNORECASE) temp = re.sub(basis_block, process_basis_block, temp) # Process "basis file ... " file_pid = re.compile(r'(\s*?)filename\s*(\b.*\b)\s*$', re.MULTILINE | re.IGNORECASE) temp = re.sub(file_pid, process_filename, temp) # imports imports = 'from psi4 import *\n' imports += 'from p4const import *\n' imports += 'from p4util import *\n' imports += 'from molutil import *\n' imports += 'from driver import *\n' imports += 'from wrappers import *\n' imports += 'from gaussian_n import *\n' imports += 'from aliases import *\n' imports += 'from functional import *\n' imports += 'from qmmm import *\n' imports += 'psi4_io = psi4.IOManager.shared_object()\n' # psirc (a baby PSIthon script that might live in ~/.psi4rc) psirc = '' homedir = os.path.expanduser('~') psirc_file = homedir + '/.psi4rc' if os.path.isfile(psirc_file): fh = open(psirc_file) psirc = fh.read() fh.close() # Override scratch directory if user specified via env_var scratch = '' scratch_env = psi4.Process.environment['PSI_SCRATCH'] if len(scratch_env): scratch += 'psi4_io.set_default_path("%s")\n' % (scratch_env) blank_mol = 'geometry("""\n' blank_mol += '0 1\nH\nH 1 0.74\n' blank_mol += '""","blank_molecule_psi4_yo")\n' temp = imports + psirc + scratch + blank_mol + temp return temp
def _nbody_gufunc(func, method_string, **kwargs): """ Computes the nbody interaction energy, gradient, or Hessian depending on input. Parameters ---------- func : python function Python function that accepts method_string and a molecule and returns a energy, gradient, or Hessian. method_string : str Lowername to be passed to function molecule : psi4.Molecule (default: Global Molecule) Molecule to use in all computations return_wfn : bool (default: False) Return a wavefunction or not bsse_type : str or list (default: None, this function is not called) Type of BSSE correction to compute: CP, NoCP, or VMFC. The first in this list is returned by this function. max_nbody : int Maximum n-body to compute, cannot exceede the number of fragments in the moleucle ptype : str Type of the procedure passed in return_total_data : bool (default: False) If True returns the total data (energy/gradient/etc) of the system otherwise returns interaction data Returns ------- data : return type of func The interaction data wfn : psi4.Wavefunction (optional) A wavefunction with energy/gradient/hessian set appropriotely. This wavefunction also contains Notes ----- This is a generalized univeral function for compute interaction quantities. Examples -------- """ ### ==> Parse some kwargs <== kwargs = p4util.kwargs_lower(kwargs) return_wfn = kwargs.pop('return_wfn', False) ptype = kwargs.pop('ptype', None) return_total_data = kwargs.pop('return_total_data', False) molecule = kwargs.pop('molecule', psi4.get_active_molecule()) molecule.update_geometry() psi4.clean_variables() if ptype not in ['energy', 'gradient', 'hessian']: raise ValidationError("""N-Body driver: The ptype '%s' is not regonized.""" % ptype) # Figure out BSSE types do_cp = False do_nocp = False do_vmfc = False return_method = False # Must be passed bsse_type bsse_type_list = kwargs.pop('bsse_type') if bsse_type_list is None: raise ValidationError("N-Body GUFunc: Must pass a bsse_type") if not isinstance(bsse_type_list, list): bsse_type_list = [bsse_type_list] for num, btype in enumerate(bsse_type_list): if btype.lower() == 'cp': do_cp = True if (num == 0): return_method = 'cp' elif btype.lower() == 'nocp': do_nocp = True if (num == 0): return_method = 'nocp' elif btype.lower() == 'vmfc': do_vmfc = True if (num == 0): return_method = 'vmfc' else: raise ValidationError("N-Body GUFunc: bsse_type '%s' is not recognized" % btype.lower()) max_nbody = kwargs.get('max_nbody', -1) max_frag = molecule.nfragments() if max_nbody == -1: max_nbody = molecule.nfragments() else: max_nbody = min(max_nbody, max_frag) # What levels do we need? nbody_range = range(1, max_nbody + 1) fragment_range = range(1, max_frag + 1) # If we are doing CP lets save them integrals if 'cp' in bsse_type_list and (len(bsse_type_list) == 1): # Set to save RI integrals for repeated full-basis computations ri_ints_io = psi4.get_global_option('DF_INTS_IO') # inquire if above at all applies to dfmp2 or just scf psi4.set_global_option('DF_INTS_IO', 'SAVE') psioh = psi4.IOManager.shared_object() psioh.set_specific_retention(97, True) bsse_str = bsse_type_list[0] if len(bsse_type_list) >1: bsse_str = str(bsse_type_list) psi4.print_out("\n\n") psi4.print_out(" ===> N-Body Interaction Abacus <===\n") psi4.print_out(" BSSE Treatment: %s\n" % bsse_str) cp_compute_list = {x:set() for x in nbody_range} nocp_compute_list = {x:set() for x in nbody_range} vmfc_compute_list = {x:set() for x in nbody_range} vmfc_level_list = {x:set() for x in nbody_range} # Need to sum something slightly different # Build up compute sets if do_cp: # Everything is in dimer basis basis_tuple = tuple(fragment_range) for nbody in nbody_range: for x in it.combinations(fragment_range, nbody): cp_compute_list[nbody].add( (x, basis_tuple) ) if do_nocp: # Everything in monomer basis for nbody in nbody_range: for x in it.combinations(fragment_range, nbody): nocp_compute_list[nbody].add( (x, x) ) if do_vmfc: # Like a CP for all combinations of pairs or greater for nbody in nbody_range: for cp_combos in it.combinations(fragment_range, nbody): basis_tuple = tuple(cp_combos) for interior_nbody in nbody_range: for x in it.combinations(cp_combos, interior_nbody): combo_tuple = (x, basis_tuple) vmfc_compute_list[interior_nbody].add( combo_tuple ) vmfc_level_list[len(basis_tuple)].add( combo_tuple ) # Build a comprehensive compute_range compute_list = {x:set() for x in nbody_range} for n in nbody_range: compute_list[n] |= cp_compute_list[n] compute_list[n] |= nocp_compute_list[n] compute_list[n] |= vmfc_compute_list[n] psi4.print_out(" Number of %d-body computations: %d\n" % (n, len(compute_list[n]))) # Build size and slices dictionaries fragment_size_dict = {frag: molecule.extract_subsets(frag).natom() for frag in range(1, max_frag+1)} start = 0 fragment_slice_dict = {} for k, v in fragment_size_dict.items(): fragment_slice_dict[k] = slice(start, start + v) start += v molecule_total_atoms = sum(fragment_size_dict.values()) # Now compute the energies energies_dict = {} ptype_dict = {} for n in compute_list.keys(): psi4.print_out("\n ==> N-Body: Now computing %d-body complexes <==\n\n" % n) print("\n ==> N-Body: Now computing %d-body complexes <==\n" % n) total = len(compute_list[n]) for num, pair in enumerate(compute_list[n]): psi4.print_out("\n N-Body: Computing complex (%d/%d) with fragments %s in the basis of fragments %s.\n\n" % (num + 1, total, str(pair[0]), str(pair[1]))) ghost = list(set(pair[1]) - set(pair[0])) current_mol = molecule.extract_subsets(list(pair[0]), ghost) ptype_dict[pair] = func(method_string, molecule=current_mol, **kwargs) energies_dict[pair] = psi4.get_variable("CURRENT ENERGY") psi4.print_out("\n N-Body: Complex Energy (fragments = %s, basis = %s: %20.14f)\n" % (str(pair[0]), str(pair[1]), energies_dict[pair])) if 'cp' in bsse_type_list and (len(bsse_type_list) == 1): psi4.set_global_option('DF_INTS_IO', 'LOAD') psi4.clean() # Final dictionaries cp_energy_by_level = {n: 0.0 for n in nbody_range} nocp_energy_by_level = {n: 0.0 for n in nbody_range} cp_energy_body_dict = {n: 0.0 for n in nbody_range} nocp_energy_body_dict = {n: 0.0 for n in nbody_range} vmfc_energy_body_dict = {n: 0.0 for n in nbody_range} # Build out ptype dictionaries if needed if ptype != 'energy': if ptype == 'gradient': arr_shape = (molecule_total_atoms, 3) elif ptype == 'hessian': arr_shape = (molecule_total_atoms * 3, molecule_total_atoms * 3) else: raise KeyError("N-Body: ptype '%s' not recognized" % ptype) cp_ptype_by_level = {n: np.zeros(arr_shape) for n in nbody_range} nocp_ptype_by_level = {n: np.zeros(arr_shape) for n in nbody_range} cp_ptype_body_dict = {n: np.zeros(arr_shape) for n in nbody_range} nocp_ptype_body_dict = {n: np.zeros(arr_shape) for n in nbody_range} vmfc_ptype_body_dict = {n: np.zeros(arr_shape) for n in nbody_range} else: cp_ptype_by_level, cp_ptype_body_dict = None, None nocp_ptype_by_level, nocp_ptype_body_dict = None, None vmfc_ptype_by_level= None # Sum up all of the levels for n in nbody_range: # Energy cp_energy_by_level[n] = sum(energies_dict[v] for v in cp_compute_list[n]) nocp_energy_by_level[n] = sum(energies_dict[v] for v in nocp_compute_list[n]) # Special vmfc case if n > 1: vmfc_energy_body_dict[n] = vmfc_energy_body_dict[n - 1] for tup in vmfc_level_list[n]: vmfc_energy_body_dict[n] += ((-1) ** (n - len(tup[0]))) * energies_dict[tup] # Do ptype if ptype != 'energy': _sum_cluster_ptype_data(ptype, ptype_dict, cp_compute_list[n], fragment_slice_dict, fragment_size_dict, cp_ptype_by_level[n]) _sum_cluster_ptype_data(ptype, ptype_dict, nocp_compute_list[n], fragment_slice_dict, fragment_size_dict, nocp_ptype_by_level[n]) _sum_cluster_ptype_data(ptype, ptype_dict, vmfc_level_list[n], fragment_slice_dict, fragment_size_dict, vmfc_ptype_by_level[n], vmfc=True) # Compute cp energy and ptype if do_cp: for n in nbody_range: if n == max_frag: cp_energy_body_dict[n] = cp_energy_by_level[n] if ptype != 'energy': cp_ptype_body_dict[n][:] = cp_ptype_by_level[n] continue for k in range(1, n + 1): take_nk = nCr(max_frag - k - 1, n - k) sign = ((-1) ** (n - k)) value = cp_energy_by_level[k] cp_energy_body_dict[n] += take_nk * sign * value if ptype != 'energy': value = cp_ptype_by_level[k] cp_ptype_body_dict[n] += take_nk * sign * value _print_nbody_energy(cp_energy_body_dict, "Counterpoise Corrected (CP)") cp_interaction_energy = cp_energy_body_dict[max_nbody] - cp_energy_body_dict[1] psi4.set_variable('Counterpoise Corrected Total Energy', cp_energy_body_dict[max_nbody]) psi4.set_variable('Counterpoise Corrected Interaction Energy', cp_interaction_energy) for n in nbody_range[1:]: var_key = 'CP-CORRECTED %d-BODY INTERACTION ENERGY' % n psi4.set_variable(var_key, cp_energy_body_dict[n] - cp_energy_body_dict[1]) # Compute nocp energy and ptype if do_nocp: for n in nbody_range: if n == max_frag: nocp_energy_body_dict[n] = nocp_energy_by_level[n] if ptype != 'energy': nocp_ptype_body_dict[n][:] = nocp_ptype_by_level[n] continue for k in range(1, n + 1): take_nk = nCr(max_frag - k - 1, n - k) sign = ((-1) ** (n - k)) value = nocp_energy_by_level[k] nocp_energy_body_dict[n] += take_nk * sign * value if ptype != 'energy': value = nocp_ptype_by_level[k] nocp_ptype_body_dict[n] += take_nk * sign * value _print_nbody_energy(nocp_energy_body_dict, "Non-Counterpoise Corrected (NoCP)") nocp_interaction_energy = nocp_energy_body_dict[max_nbody] - nocp_energy_body_dict[1] psi4.set_variable('Non-Counterpoise Corrected Total Energy', nocp_energy_body_dict[max_nbody]) psi4.set_variable('Non-Counterpoise Corrected Interaction Energy', nocp_interaction_energy) for n in nbody_range[1:]: var_key = 'NOCP-CORRECTED %d-BODY INTERACTION ENERGY' % n psi4.set_variable(var_key, nocp_energy_body_dict[n] - nocp_energy_body_dict[1]) # Compute vmfc energy and ptype if do_vmfc: _print_nbody_energy(vmfc_energy_body_dict, "Valiron-Mayer Function Couterpoise (VMFC)") vmfc_interaction_energy = vmfc_energy_body_dict[max_nbody] - vmfc_energy_body_dict[1] psi4.set_variable('Valiron-Mayer Function Couterpoise Total Energy', vmfc_energy_body_dict[max_nbody]) psi4.set_variable('Valiron-Mayer Function Couterpoise Interaction Energy', vmfc_interaction_energy) for n in nbody_range[1:]: var_key = 'VMFC-CORRECTED %d-BODY INTERACTION ENERGY' % n psi4.set_variable(var_key, vmfc_energy_body_dict[n] - vmfc_energy_body_dict[1]) if return_method == 'cp': ptype_body_dict = cp_ptype_body_dict energy_body_dict = cp_energy_body_dict elif return_method == 'nocp': ptype_body_dict = nocp_ptype_body_dict energy_body_dict = nocp_energy_body_dict elif return_method == 'vmfc': ptype_body_dict = vmfc_ptype_body_dict energy_body_dict = vmfc_energy_body_dict else: raise ValidationError("N-Body Wrapper: Invalid return type. Should never be here, please post this error on github.") # Figure out and build return types if return_total_data: ret_energy = energy_body_dict[max_nbody] else: ret_energy = energy_body_dict[max_nbody] ret_energy -= energy_body_dict[1] if ptype != 'energy': if return_total_data: np_final_ptype = ptype_body_dict[max_nbody].copy() else: np_final_ptype = ptype_body_dict[max_nbody].copy() np_final_ptype -= ptype_body_dict[1] ret_ptype = psi4.Matrix(*np_cp_final_ptype.shape) ret_ptype_view = np.asarray(final_ptype) ret_ptype_view[:] = np_final_ptype else: ret_ptype = ret_energy # Build and set a wavefunction wfn = psi4.new_wavefunction(molecule, 'sto-3g') wfn.nbody_energy = energies_dict wfn.nbody_ptype = ptype_dict wfn.nbody_body_energy = energy_body_dict wfn.nbody_body_ptype = ptype_body_dict if ptype == 'gradient': wfn.set_gradient(ret_ptype) elif ptype == 'hessian': wfn.set_hessian(ret_ptype) psi4.set_variable("CURRENT ENERGY", ret_energy) if return_wfn: return (ret_ptype, wfn) else: return ret_ptype
def ip_fitting(molecule, omega_l, omega_r, **kwargs): kwargs = p4util.kwargs_lower(kwargs) # By default, zero the omega to 3 digits omega_tol = kwargs.get('omega_tolerance', 1.0E-3) # By default, do up to twenty iterations maxiter = kwargs.get('maxiter', 20) # By default, do not read previous 180 orbitals file read = False read180 = '' if 'read' in kwargs: read = True read180 = kwargs['read'] # The molecule is required, and should be the neutral species molecule.update_geometry() charge0 = molecule.molecular_charge() mult0 = molecule.multiplicity() # How many electrons are there? N = 0 for A in range(molecule.natom()): N += molecule.Z(A) N -= charge0 N = int(N) Nb = int((N - mult0 + 1) / 2) Na = int(N - Nb) # Work in the ot namespace for this procedure psi4.IO.set_default_namespace("ot") # Burn in to determine orbital eigenvalues if read: psi4.set_global_option("GUESS", "READ") copy_file_to_scratch(read180, 'psi', 'ot', 180) old_guess = psi4.get_global_option("GUESS") psi4.set_global_option("DF_INTS_IO", "SAVE") psi4.print_out("""\n\t==> IP Fitting SCF: Burn-in <==\n""") E, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) psi4.set_global_option("DF_INTS_IO", "LOAD") # Determine H**O, to determine mult1 eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() if Na == Nb: H**O = -Nb elif Nb == 0: H**O = Na else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if E_a >= E_b: H**O = Na else: H**O = -Nb Na1 = Na Nb1 = Nb if H**O > 0: Na1 = Na1 - 1 else: Nb1 = Nb1 - 1 charge1 = charge0 + 1 mult1 = Na1 - Nb1 + 1 omegas = [] E0s = [] E1s = [] kIPs = [] IPs = [] types = [] # Right endpoint psi4.set_global_option('DFT_OMEGA', omega_r) # Neutral if read: psi4.set_global_option("GUESS", "READ") p4util.copy_file_to_scratch(read180, 'psi', 'ot', 180) molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) psi4.print_out("""\n\t==> IP Fitting SCF: Neutral, Right Endpoint <==\n""") E0r, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() E_HOMO = 0.0 if Nb == 0: E_HOMO = eps_a[int(Na - 1)] else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if E_a >= E_b: E_HOMO = E_a else: E_HOMO = E_b E_HOMOr = E_HOMO psi4.IO.change_file_namespace(180, "ot", "neutral") # Cation if read: psi4.set_global_option("GUESS", "READ") p4util.copy_file_to_scratch(read180, 'psi', 'ot', 180) molecule.set_molecular_charge(charge1) molecule.set_multiplicity(mult1) psi4.print_out("""\n\t==> IP Fitting SCF: Cation, Right Endpoint <==\n""") E1r = energy('scf', molecule=molecule, **kwargs) psi4.IO.change_file_namespace(180, "ot", "cation") IPr = E1r - E0r kIPr = -E_HOMOr delta_r = IPr - kIPr if IPr > kIPr: message = ( """\n***IP Fitting Error: Right Omega limit should have kIP > IP""" ) raise ValidationError(message) omegas.append(omega_r) types.append('Right Limit') E0s.append(E0r) E1s.append(E1r) IPs.append(IPr) kIPs.append(kIPr) # Use previous orbitals from here out psi4.set_global_option("GUESS", "READ") # Left endpoint psi4.set_global_option('DFT_OMEGA', omega_l) # Neutral psi4.IO.change_file_namespace(180, "neutral", "ot") molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) psi4.print_out("""\n\t==> IP Fitting SCF: Neutral, Left Endpoint <==\n""") E0l, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() E_HOMO = 0.0 if Nb == 0: E_HOMO = eps_a[int(Na - 1)] else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if E_a >= E_b: E_HOMO = E_a else: E_HOMO = E_b E_HOMOl = E_HOMO psi4.IO.change_file_namespace(180, "ot", "neutral") # Cation psi4.IO.change_file_namespace(180, "cation", "ot") molecule.set_molecular_charge(charge1) molecule.set_multiplicity(mult1) psi4.print_out("""\n\t==> IP Fitting SCF: Cation, Left Endpoint <==\n""") E1l = energy('scf', molecule=molecule, **kwargs) psi4.IO.change_file_namespace(180, "ot", "cation") IPl = E1l - E0l kIPl = -E_HOMOl delta_l = IPl - kIPl if IPl < kIPl: message = ( """\n***IP Fitting Error: Left Omega limit should have kIP < IP""") raise ValidationError(message) omegas.append(omega_l) types.append('Left Limit') E0s.append(E0l) E1s.append(E1l) IPs.append(IPl) kIPs.append(kIPl) converged = False repeat_l = 0 repeat_r = 0 step = 0 while True: step = step + 1 # Regula Falsi (modified) if repeat_l > 1: delta_l = delta_l / 2.0 if repeat_r > 1: delta_r = delta_r / 2.0 omega = -(omega_r - omega_l) / (delta_r - delta_l) * delta_l + omega_l psi4.set_global_option('DFT_OMEGA', omega) # Neutral psi4.IO.change_file_namespace(180, "neutral", "ot") molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) psi4.print_out( """\n\t==> IP Fitting SCF: Neutral, Omega = %11.3E <==\n""" % omega) E0, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() E_HOMO = 0.0 if Nb == 0: E_HOMO = eps_a[int(Na - 1)] else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if E_a >= E_b: E_HOMO = E_a else: E_HOMO = E_b psi4.IO.change_file_namespace(180, "ot", "neutral") # Cation psi4.IO.change_file_namespace(180, "cation", "ot") molecule.set_molecular_charge(charge1) molecule.set_multiplicity(mult1) psi4.print_out( """\n\t==> IP Fitting SCF: Cation, Omega = %11.3E <==\n""" % omega) E1 = energy('scf', molecule=molecule, **kwargs) psi4.IO.change_file_namespace(180, "ot", "cation") IP = E1 - E0 kIP = -E_HOMO delta = IP - kIP if kIP > IP: omega_r = omega E0r = E0 E1r = E1 IPr = IP kIPr = kIP delta_r = delta repeat_r = 0 repeat_l = repeat_l + 1 else: omega_l = omega E0l = E0 E1l = E1 IPl = IP kIPl = kIP delta_l = delta repeat_l = 0 repeat_r = repeat_r + 1 omegas.append(omega) types.append('Regula-Falsi') E0s.append(E0) E1s.append(E1) IPs.append(IP) kIPs.append(kIP) # Termination if (abs(omega_l - omega_r) < omega_tol or step > maxiter): converged = True break # Properly, should clone molecule but since not returned and easy to unblemish, molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) psi4.IO.set_default_namespace("") psi4.print_out("""\n\t==> IP Fitting Results <==\n\n""") psi4.print_out("""\t => Occupation Determination <= \n\n""") psi4.print_out("""\t %6s %6s %6s %6s %6s %6s\n""" % ('N', 'Na', 'Nb', 'Charge', 'Mult', 'H**O')) psi4.print_out("""\t Neutral: %6d %6d %6d %6d %6d %6d\n""" % (N, Na, Nb, charge0, mult0, H**O)) psi4.print_out("""\t Cation: %6d %6d %6d %6d %6d\n\n""" % (N - 1, Na1, Nb1, charge1, mult1)) psi4.print_out("""\t => Regula Falsi Iterations <=\n\n""") psi4.print_out("""\t%3s %11s %14s %14s %14s %s\n""" % ('N', 'Omega', 'IP', 'kIP', 'Delta', 'Type')) for k in range(len(omegas)): psi4.print_out( """\t%3d %11.3E %14.6E %14.6E %14.6E %s\n""" % (k + 1, omegas[k], IPs[k], kIPs[k], IPs[k] - kIPs[k], types[k])) if converged: psi4.print_out("""\n\tIP Fitting Converged\n""") psi4.print_out("""\tFinal omega = %14.6E\n""" % ((omega_l + omega_r) / 2)) psi4.print_out( """\n\t"M,I. does the dying. Fleet just does the flying."\n""") psi4.print_out("""\t\t\t-Starship Troopers\n""") else: psi4.print_out("""\n\tIP Fitting did not converge!\n""") psi4.set_global_option("DF_INTS_IO", "NONE") psi4.set_global_option("GUESS", old_guess)
def __init__(self, msg): PsiException.__init__(self, msg) self.msg = msg psi4.print_out('\nPsiException: %s\n\n' % (msg))
def ip_fitting(mol, omega_l, omega_r, **kwargs): kwargs = p4util.kwargs_lower(kwargs) # By default, zero the omega to 3 digits omega_tol = 1.0E-3; if (kwargs.has_key('omega_tolerance')): omega_tol = kwargs['omega_tolerance'] # By default, do up to twenty iterations maxiter = 20; if (kwargs.has_key('maxiter')): maxiter = kwargs['maxiter'] # By default, do not read previous 180 orbitals file read = False; read180 = '' if (kwargs.has_key('read')): read = True; read180 = kwargs['read'] # The molecule is required, and should be the neutral species mol.update_geometry() activate(mol) charge0 = mol.molecular_charge() mult0 = mol.multiplicity() # How many electrons are there? N = 0; for A in range(mol.natom()): N += mol.Z(A) N -= charge0 N = int(N) Nb = int((N - mult0 + 1)/2) Na = int(N - Nb) # Work in the ot namespace for this procedure psi4.IO.set_default_namespace("ot") # Burn in to determine orbital eigenvalues if (read): psi4.set_global_option("GUESS", "READ") copy_file_to_scratch(read180, 'psi', 'ot', 180) old_guess = psi4.get_global_option("GUESS") psi4.set_global_option("DF_INTS_IO", "SAVE") psi4.print_out('\n\t==> IP Fitting SCF: Burn-in <==\n') energy('scf') psi4.set_global_option("DF_INTS_IO", "LOAD") # Determine H**O, to determine mult1 ref = psi4.wavefunction() eps_a = ref.epsilon_a() eps_b = ref.epsilon_b() if (Na == Nb): H**O = -Nb elif (Nb == 0): H**O = Na else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if (E_a >= E_b): H**O = Na else: H**O = -Nb Na1 = Na; Nb1 = Nb; if (H**O > 0): Na1 = Na1-1; else: Nb1 = Nb1-1; charge1 = charge0 + 1; mult1 = Na1 - Nb1 + 1 omegas = []; E0s = []; E1s = []; kIPs = []; IPs = []; types = []; # Right endpoint psi4.set_global_option('DFT_OMEGA',omega_r) # Neutral if (read): psi4.set_global_option("GUESS", "READ") p4util.copy_file_to_scratch(read180, 'psi', 'ot', 180) mol.set_molecular_charge(charge0) mol.set_multiplicity(mult0) psi4.print_out('\n\t==> IP Fitting SCF: Neutral, Right Endpoint <==\n') E0r = energy('scf') ref = psi4.wavefunction() eps_a = ref.epsilon_a() eps_b = ref.epsilon_b() E_HOMO = 0.0; if (Nb == 0): E_HOMO = eps_a[int(Na-1)] else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if (E_a >= E_b): E_HOMO = E_a; else: E_HOMO = E_b; E_HOMOr = E_HOMO; psi4.IO.change_file_namespace(180,"ot","neutral") # Cation if (read): psi4.set_global_option("GUESS", "READ") p4util.copy_file_to_scratch(read180, 'psi', 'ot', 180) mol.set_molecular_charge(charge1) mol.set_multiplicity(mult1) psi4.print_out('\n\t==> IP Fitting SCF: Cation, Right Endpoint <==\n') E1r = energy('scf') psi4.IO.change_file_namespace(180,"ot","cation") IPr = E1r - E0r; kIPr = -E_HOMOr; delta_r = IPr - kIPr; if (IPr > kIPr): psi4.print_out('\n***IP Fitting Error: Right Omega limit should have kIP > IP') sys.exit(1) omegas.append(omega_r) types.append('Right Limit') E0s.append(E0r) E1s.append(E1r) IPs.append(IPr) kIPs.append(kIPr) # Use previous orbitals from here out psi4.set_global_option("GUESS","READ") # Left endpoint psi4.set_global_option('DFT_OMEGA',omega_l) # Neutral psi4.IO.change_file_namespace(180,"neutral","ot") mol.set_molecular_charge(charge0) mol.set_multiplicity(mult0) psi4.print_out('\n\t==> IP Fitting SCF: Neutral, Left Endpoint <==\n') E0l = energy('scf') ref = psi4.wavefunction() eps_a = ref.epsilon_a() eps_b = ref.epsilon_b() E_HOMO = 0.0; if (Nb == 0): E_HOMO = eps_a[int(Na-1)] else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if (E_a >= E_b): E_HOMO = E_a; else: E_HOMO = E_b; E_HOMOl = E_HOMO; psi4.IO.change_file_namespace(180,"ot","neutral") # Cation psi4.IO.change_file_namespace(180,"cation","ot") mol.set_molecular_charge(charge1) mol.set_multiplicity(mult1) psi4.print_out('\n\t==> IP Fitting SCF: Cation, Left Endpoint <==\n') E1l = energy('scf') psi4.IO.change_file_namespace(180,"ot","cation") IPl = E1l - E0l; kIPl = -E_HOMOl; delta_l = IPl - kIPl; if (IPl < kIPl): psi4.print_out('\n***IP Fitting Error: Left Omega limit should have kIP < IP') sys.exit(1) omegas.append(omega_l) types.append('Left Limit') E0s.append(E0l) E1s.append(E1l) IPs.append(IPl) kIPs.append(kIPl) converged = False repeat_l = 0; repeat_r = 0; step = 0; while True: step = step + 1; # Regula Falsi (modified) if (repeat_l > 1): delta_l = delta_l / 2.0; if (repeat_r > 1): delta_r = delta_r / 2.0; omega = - (omega_r - omega_l) / (delta_r - delta_l) * delta_l + omega_l; psi4.set_global_option('DFT_OMEGA',omega) # Neutral psi4.IO.change_file_namespace(180,"neutral","ot") mol.set_molecular_charge(charge0) mol.set_multiplicity(mult0) psi4.print_out('\n\t==> IP Fitting SCF: Neutral, Omega = %11.3E <==\n' % omega) E0 = energy('scf') ref = psi4.wavefunction() eps_a = ref.epsilon_a() eps_b = ref.epsilon_b() E_HOMO = 0.0; if (Nb == 0): E_HOMO = eps_a[int(Na-1)] else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if (E_a >= E_b): E_HOMO = E_a; else: E_HOMO = E_b; psi4.IO.change_file_namespace(180,"ot","neutral") # Cation psi4.IO.change_file_namespace(180,"cation","ot") mol.set_molecular_charge(charge1) mol.set_multiplicity(mult1) psi4.print_out('\n\t==> IP Fitting SCF: Cation, Omega = %11.3E <==\n' % omega) E1 = energy('scf') psi4.IO.change_file_namespace(180,"ot","cation") IP = E1 - E0; kIP = -E_HOMO; delta = IP - kIP; if (kIP > IP): omega_r = omega E0r = E0 E1r = E1 IPr = IP kIPr = kIP delta_r = delta repeat_r = 0; repeat_l = repeat_l + 1; else: omega_l = omega E0l = E0 E1l = E1 IPl = IP kIPl = kIP delta_l = delta repeat_l = 0; repeat_r = repeat_r + 1; omegas.append(omega) types.append('Regula-Falsi') E0s.append(E0) E1s.append(E1) IPs.append(IP) kIPs.append(kIP) # Termination if (abs(omega_l - omega_r) < omega_tol or step > maxiter): converged = True; break psi4.IO.set_default_namespace("") psi4.print_out('\n\t==> IP Fitting Results <==\n\n') psi4.print_out('\t => Occupation Determination <= \n\n') psi4.print_out('\t %6s %6s %6s %6s %6s %6s\n' %('N', 'Na', 'Nb', 'Charge', 'Mult', 'H**O')) psi4.print_out('\t Neutral: %6d %6d %6d %6d %6d %6d\n' %(N, Na, Nb, charge0, mult0, H**O)) psi4.print_out('\t Cation: %6d %6d %6d %6d %6d\n\n' %(N-1, Na1, Nb1, charge1, mult1)) psi4.print_out('\t => Regula Falsi Iterations <=\n\n') psi4.print_out('\t%3s %11s %14s %14s %14s %s\n' % ('N','Omega','IP','kIP','Delta','Type')) for k in range(len(omegas)): psi4.print_out('\t%3d %11.3E %14.6E %14.6E %14.6E %s\n' % (k+1,omegas[k],IPs[k],kIPs[k],IPs[k] - kIPs[k], types[k])) if (converged): psi4.print_out('\n\tIP Fitting Converged\n') psi4.print_out('\tFinal omega = %14.6E\n' % ((omega_l + omega_r) / 2)) psi4.print_out('\n\t"M,I. does the dying. Fleet just does the flying."\n') psi4.print_out('\t\t\t-Starship Troopers\n') else: psi4.print_out('\n\tIP Fitting did not converge!\n') psi4.set_global_option("DF_INTS_IO", "NONE") psi4.set_global_option("GUESS", old_guess)
def ccsd_energy(self): indx, Ep1, Ep2, g = self.indx, self.Ep1, self.Ep2, self.g t1 = indx.zeros('ia') t2 = indx.einsum('ijab', (g, "ijab"), (Ep2, "ijab")) for i in range(maxiter): t1 = Ep1 * indx.meinsums( 'ia', # Crawford p. 59 [1., I, (g, "kaci"), (t1, "kc")], [1. / 2, I, (g, "kacd"), (t2, "kicd")], [-1. / 2, I, (g, "klci"), (t2, "klca")], [-1., I, (g, "klci"), (t1, "kc"), (t1, "la")], [1., I, (g, "kacd"), (t1, "kc"), (t1, "id")], [-1., I, (g, "klcd"), (t1, "kc"), (t1, "id"), (t1, "la")], [1., I, (g, "klcd"), (t1, "kc"), (t2, "lida")], [-1. / 2, I, (g, "klcd"), (t2, "kicd"), (t1, "la")], [-1. / 2, I, (g, "klcd"), (t2, "klca"), (t1, "id")]) t2 = Ep2 * indx.meinsums( 'ijab', # Crawford, p. 59-60 [1., I, (g, "abij")], [1. / 2, I, (g, "klij"), (t2, "klab")], [1. / 2, I, (g, "abcd"), (t2, "ijcd")], [1., P("ij|ab"), (g, "kbcj"), (t2, "ikac")], [1., P("ij"), (g, "abcj"), (t1, "ic")], [-1., P("ab"), (g, "kbij"), (t1, "ka")], [1. / 2, P("ij|ab"), (g, "klcd"), (t2, "ikac"), (t2, "ljdb")], [1. / 4, I, (g, "klcd"), (t2, "ijcd"), (t2, "klab")], [-1. / 2, P("ab"), (g, "klcd"), (t2, "ijac"), (t2, "klbd")], [-1. / 2, P("ij"), (g, "klcd"), (t2, "ikab"), (t2, "jlcd")], [1. / 2, P("ab"), (g, "klij"), (t1, "ka"), (t1, "lb")], [1. / 2, P("ij"), (g, "abcd"), (t1, "ic"), (t1, "jd")], [-1., P("ij|ab"), (g, "kbic"), (t1, "ka"), (t1, "jc")], [-1., P("ij"), (g, "klci"), (t1, "kc"), (t2, "ljab")], [1., P("ab"), (g, "kacd"), (t1, "kc"), (t2, "ijdb")], [1., P("ij|ab"), (g, "akdc"), (t1, "id"), (t2, "jkbc")], [1., P("ij|ab"), (g, "klic"), (t1, "la"), (t2, "jkbc")], [1. / 2, P("ij"), (g, "klcj"), (t1, "ic"), (t2, "klab")], [-1. / 2, P("ab"), (g, "kbcd"), (t1, "ka"), (t2, "ijcd")], [ -1. / 2, P("ij|ab"), (g, "kbcd"), (t1, "ic"), (t1, "ka"), (t1, "jd") ], [ 1. / 2, P("ij|ab"), (g, "klcj"), (t1, "ic"), (t1, "ka"), (t1, "lb") ], [ -1., P("ij"), (g, "klcd"), (t1, "kc"), (t1, "id"), (t2, "ljab") ], [ -1., P("ab"), (g, "klcd"), (t1, "kc"), (t1, "la"), (t2, "ijdb") ], [ 1. / 4, P("ij"), (g, "klcd"), (t1, "ic"), (t1, "jd"), (t2, "klab") ], [ 1. / 4, P("ab"), (g, "klcd"), (t1, "ka"), (t1, "lb"), (t2, "ijcd") ], [ 1., P("ij|ab"), (g, "klcd"), (t1, "ic"), (t1, "lb"), (t2, "kjad") ], [ 1. / 4, P("ij|ab"), (g, "klcd"), (t1, "ic"), (t1, "ka"), (t1, "jd"), (t1, "lb") ]) E = indx.meinsums('', [1. / 4, I, (g, "ijab"), (t2, "ijab")], [1. / 2, I, (g, "ijab"), (t1, "ia"), (t1, "jb")]) dE = E - self.E self.E = E psi4.print_out('\n@CCSD{:-3d}{:20.15f}{:20.15f}'.format(i, E, dE)) if (abs(dE) < econv): break return self.E
def anharmonicity(rvals, energies, mol=None): """Generates spectroscopic constants for a diatomic molecules. Fits a diatomic potential energy curve using either a 5 or 9 point Legendre fit, locates the minimum energy point, and then applies second order vibrational perturbation theory to obtain spectroscopic constants. The r values provided must bracket the minimum energy point, or an error will result. A dictionary with the following keys, which correspond to spectroscopic constants, is returned: :type rvals: list :param rvals: The bond lengths (in Angstrom) for which energies are provided of length either 5 or 9 but must be the same length as the energies array :type energies: list :param energies: The energies (Eh) computed at the bond lengths in the rvals list :returns: (*dict*) Keys: "re", "r0", "we", "wexe", "nu", "ZPVE(harmonic)", "ZPVE(anharmonic)", "Be", "B0", "ae", "De" corresponding to the spectroscopic constants in cm-1 """ angstrom_to_bohr = 1.0 / p4const.psi_bohr2angstroms angstrom_to_meter = 10e-10 if len(rvals) != len(energies): raise Exception( "The number of energies must match the number of distances") npoints = len(rvals) if npoints != 5 and npoints != 9: raise Exception("Only 5- or 9-point fits are implemented right now") psi4.print_out("\n\nPerforming a %d-point fit\n" % npoints) psi4.print_out("\nOptimizing geometry based on current surface:\n\n") if (npoints == 5): optx = rvals[2] elif (npoints == 9): optx = rvals[4] # Molecule can be passed in be user. Look at the function definition above. if mol == None: mol = psi4.get_active_molecule() natoms = mol.natom() if natoms != 2: raise Exception( "The current molecule must be a diatomic for this code to work!") m1 = mol.mass(0) m2 = mol.mass(1) maxit = 30 thres = 1.0e-9 for i in range(maxit): if (npoints == 5): grad = first_deriv_5pt(rvals, energies, optx) secd = second_deriv_5pt(rvals, energies, optx) energy = function_5pt(rvals, energies, optx) elif (npoints == 9): grad = first_deriv_9pt(rvals, energies, optx) secd = second_deriv_9pt(rvals, energies, optx) energy = function_9pt(rvals, energies, optx) psi4.print_out(" E = %20.14f, x = %14.7f, grad = %20.14f\n" % (energy, optx, grad)) if abs(grad) < thres: break optx -= grad / secd psi4.print_out(" Final E = %20.14f, x = %14.7f, grad = %20.14f\n" % (function_5pt(rvals, energies, optx), optx, grad)) if optx < min(rvals): raise Exception( "Minimum energy point is outside range of points provided. Use a lower range of r values." ) if optx > max(rvals): raise Exception( "Minimum energy point is outside range of points provided. Use a higher range of r values." ) if (npoints == 5): energy = function_5pt(rvals, energies, optx) first = first_deriv_5pt(rvals, energies, optx) secd = second_deriv_5pt(rvals, energies, optx) * p4const.psi_hartree2aJ third = third_deriv_5pt(rvals, energies, optx) * p4const.psi_hartree2aJ fourth = fourth_deriv_5pt(rvals, energies, optx) * p4const.psi_hartree2aJ elif (npoints == 9): energy = function_9pt(rvals, energies, optx) first = first_deriv_9pt(rvals, energies, optx) secd = second_deriv_9pt(rvals, energies, optx) * p4const.psi_hartree2aJ third = third_deriv_9pt(rvals, energies, optx) * p4const.psi_hartree2aJ fourth = fourth_deriv_9pt(rvals, energies, optx) * p4const.psi_hartree2aJ psi4.print_out("\nEquilibrium Energy %20.14f Hartrees\n" % energy) psi4.print_out("Gradient %20.14f\n" % first) psi4.print_out("Quadratic Force Constant %14.7f MDYNE/A\n" % secd) psi4.print_out("Cubic Force Constant %14.7f MDYNE/A**2\n" % third) psi4.print_out("Quartic Force Constant %14.7f MDYNE/A**3\n" % fourth) hbar = p4const.psi_h / (2.0 * pi) mu = ((m1 * m2) / (m1 + m2)) * p4const.psi_amu2kg we = 5.3088375e-11 * sqrt(secd / mu) wexe = (1.2415491e-6) * (we / secd)**2 * ((5.0 * third * third) / (3.0 * secd) - fourth) # Rotational constant: Be I = ((m1 * m2) / (m1 + m2)) * p4const.psi_amu2kg * (optx * angstrom_to_meter)**2 B = p4const.psi_h / (8.0 * pi**2 * p4const.psi_c * I) # alpha_e and quartic centrifugal distortion constant ae = -(6.0 * B**2 / we) * ((1.05052209e-3 * we * third) / (sqrt(B * secd**3)) + 1.0) de = 4.0 * B**3 / we**2 # B0 and r0 (plus re check using Be) B0 = B - ae / 2.0 r0 = sqrt(p4const.psi_h / (8.0 * pi**2 * mu * p4const.psi_c * B0)) recheck = sqrt(p4const.psi_h / (8.0 * pi**2 * mu * p4const.psi_c * B)) r0 /= angstrom_to_meter recheck /= angstrom_to_meter # Fundamental frequency nu nu = we - 2.0 * wexe zpve_nu = 0.5 * we - 0.25 * wexe psi4.print_out("\nre = %10.6f A check: %10.6f\n" % (optx, recheck)) psi4.print_out("r0 = %10.6f A\n" % r0) psi4.print_out("we = %10.4f cm-1\n" % we) psi4.print_out("wexe = %10.4f cm-1\n" % wexe) psi4.print_out("nu = %10.4f cm-1\n" % nu) psi4.print_out("ZPVE(nu) = %10.4f cm-1\n" % zpve_nu) psi4.print_out("Be = %10.4f cm-1\n" % B) psi4.print_out("B0 = %10.4f cm-1\n" % B0) psi4.print_out("ae = %10.4f cm-1\n" % ae) psi4.print_out("De = %10.7f cm-1\n" % de) results = { "re": optx, "r0": r0, "we": we, "wexe": wexe, "nu": nu, "ZPVE(harmonic)": zpve_nu, "ZPVE(anharmonic)": zpve_nu, "Be": B, "B0": B0, "ae": ae, "De": de } return results
def run_roa(name, **kwargs): """ Main driver for managing Raman Optical activity computations with CC response theory. Uses distributed finite differences approach --> 1. Sets up a database to keep track of running/finished/waiting computations. 2. Generates separate input files for displaced geometries. 3. When all displacements are run, collects the necessary information from each displaced computation, and computes final result. """ # Get list of omega values -> Make sure we only have one wavelength # Catch this now before any real work gets done omega = psi4.get_option('CCRESPONSE', 'OMEGA') if len(omega) > 2: raise Exception( 'ROA scattering can only be performed for one wavelength.') else: pass psi4.print_out('Running ROA computation. Subdirectories for each ' 'required displaced geometry have been created.\n\n') dbno = 0 # Initialize database db = shelve.open('database', writeback=True) # Check if final result is in here # ->if we have already computed roa, back up the dict # ->copy it setting this flag to false and continue if ('roa_computed' in db) and (db['roa_computed']): db2 = shelve.open('.database.bak{}'.format(dbno), writeback=True) dbno += 1 for key, value in db.iteritems(): db2[key] = value db2.close() db['roa_computed'] = False else: db['roa_computed'] = False if 'inputs_generated' not in db: findif_response_utils.initialize_database(db, name, "roa", ["roa_tensor"]) # Generate input files if not db['inputs_generated']: findif_response_utils.generate_inputs(db, name) # handled by helper db['inputs_generated'] = True # Check job status if db['inputs_generated'] and not db['jobs_complete']: print('Checking status') findif_response_utils.stat(db) for job, status in db['job_status'].items(): print("{} --> {}".format(job, status)) # Compute ROA Scattering if db['jobs_complete']: mygauge = psi4.get_option('CCRESPONSE', 'GAUGE') consider_gauge = { 'LENGTH': ['Length Gauge'], 'VELOCITY': ['Modified Velocity Gauge'], 'BOTH': ['Length Gauge', 'Modified Velocity Gauge'] } gauge_list = ["{} Results".format(x) for x in consider_gauge[mygauge]] # Gather data dip_polar_list = findif_response_utils.collect_displaced_matrix_data( db, 'Dipole Polarizability', 3) opt_rot_list = [ x for x in (findif_response_utils.collect_displaced_matrix_data( db, "Optical Rotation Tensor ({})".format(gauge), 3) for gauge in consider_gauge[mygauge]) ] dip_quad_polar_list = findif_response_utils.collect_displaced_matrix_data( db, "Electric-Dipole/Quadrupole Polarizability", 9) # Compute Scattering # Run new function (src/bin/ccresponse/scatter.cc) psi4.print_out('Running scatter function') step = psi4.get_local_option('FINDIF', 'DISP_SIZE') for g_idx, gauge in enumerate(opt_rot_list): print( '\n\n----------------------------------------------------------------------' ) print('\t%%%%%%%%%% {} %%%%%%%%%%'.format(gauge_list[g_idx])) print( '----------------------------------------------------------------------\n\n' ) psi4.print_out( '\n\n----------------------------------------------------------------------\n' ) psi4.print_out('\t%%%%%%%%%% {} %%%%%%%%%%\n'.format( gauge_list[g_idx])) psi4.print_out( '----------------------------------------------------------------------\n\n' ) print( 'roa.py:85 I am not being passed a molecule, grabbing from global :(' ) psi4.scatter(psi4.get_active_molecule(), step, dip_polar_list, gauge, dip_quad_polar_list) db['roa_computed'] = True db.close()
def frac_traverse(molecule, **kwargs): kwargs = p4util.kwargs_lower(kwargs) # The molecule is required, and should be the neutral species molecule.update_geometry() charge0 = molecule.molecular_charge() mult0 = molecule.multiplicity() chargep = charge0 + 1 chargem = charge0 - 1 # By default, the multiplicity of the cation/anion are mult0 + 1 # These are overridden with the cation_mult and anion_mult kwargs multp = kwargs.get('cation_mult', mult0 + 1) multm = kwargs.get('anion_mult', mult0 + 1) # By default, we start the frac procedure on the 25th iteration # when not reading a previous guess frac_start = kwargs.get('frac_start', 25) # By default, we occupy by tenths of electrons HOMO_occs = kwargs.get( 'HOMO_occs', [1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0]) LUMO_occs = kwargs.get( 'LUMO_occs', [1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0]) # By default, H**O and LUMO are both in alpha Z = 0 for A in range(molecule.natom()): Z += molecule.Z(A) Z -= charge0 H**O = kwargs.get('H**O', (Z / 2 + 1 if (Z % 2) else Z / 2)) LUMO = kwargs.get('LUMO', H**O + 1) # By default, DIIS in FRAC (1.0 occupation is always DIIS'd) frac_diis = kwargs.get('frac_diis', True) # By default, use the neutral orbitals as a guess for the anion neutral_guess = kwargs.get('neutral_guess', True) # By default, burn-in with UHF first, if UKS hf_guess = False if psi4.get_global_option('REFERENCE') == 'UKS': hf_guess = kwargs.get('hf_guess', True) # By default, re-guess at each N continuous_guess = kwargs.get('continuous_guess', False) # By default, drop the files to the molecule's name root = kwargs.get('filename', molecule.name()) traverse_filename = root + '.traverse.dat' # => Traverse <= # occs = [] energies = [] potentials = [] convs = [] # => Run the neutral for its orbitals, if requested <= # old_df_ints_io = psi4.get_global_option("DF_INTS_IO") psi4.set_global_option("DF_INTS_IO", "SAVE") old_guess = psi4.get_global_option("GUESS") if (neutral_guess): if (hf_guess): psi4.set_global_option("REFERENCE", "UHF") energy('scf') psi4.set_global_option("GUESS", "READ") psi4.set_global_option("DF_INTS_IO", "LOAD") # => Run the anion first <= # molecule.set_molecular_charge(chargem) molecule.set_multiplicity(multm) # => Burn the anion in with hf, if requested <= # if hf_guess: psi4.set_global_option("REFERENCE", "UHF") energy('scf', molecule=molecule, **kwargs) psi4.set_global_option("REFERENCE", "UKS") psi4.set_global_option("GUESS", "READ") psi4.set_global_option("DF_INTS_IO", "SAVE") psi4.set_global_option("FRAC_START", frac_start) psi4.set_global_option("FRAC_RENORMALIZE", True) psi4.set_global_option("FRAC_LOAD", False) for occ in LUMO_occs: psi4.set_global_option("FRAC_OCC", [LUMO]) psi4.set_global_option("FRAC_VAL", [occ]) E, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) C = 1 if E == 0.0: E = psi4.get_variable('SCF ITERATION ENERGY') C = 0 if LUMO > 0: eps = wfn.epsilon_a() potentials.append(eps[int(LUMO) - 1]) else: eps = wfn.epsilon_b() potentials.append(eps[-int(LUMO) - 1]) occs.append(occ) energies.append(E) convs.append(C) psi4.set_global_option("FRAC_START", 2) psi4.set_global_option("FRAC_LOAD", True) psi4.set_global_option("GUESS", "READ") psi4.set_global_option("FRAC_DIIS", frac_diis) psi4.set_global_option("DF_INTS_IO", "LOAD") # => Run the neutral next <= # molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) # Burn the neutral in with hf, if requested <= # if not continuous_guess: psi4.set_global_option("GUESS", old_guess) if hf_guess: psi4.set_global_option("FRAC_START", 0) psi4.set_global_option("REFERENCE", "UHF") energy('scf', molecule=molecule, **kwargs) psi4.set_global_option("REFERENCE", "UKS") psi4.set_global_option("GUESS", "READ") psi4.set_global_option("FRAC_LOAD", False) psi4.set_global_option("FRAC_START", frac_start) psi4.set_global_option("FRAC_RENORMALIZE", True) for occ in HOMO_occs: psi4.set_global_option("FRAC_OCC", [H**O]) psi4.set_global_option("FRAC_VAL", [occ]) E, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) C = 1 if E == 0.0: E = psi4.get_variable('SCF ITERATION ENERGY') C = 0 if LUMO > 0: eps = wfn.epsilon_a() potentials.append(eps[int(H**O) - 1]) else: eps = wfn.epsilon_b() potentials.append(eps[-int(H**O) - 1]) occs.append(occ - 1.0) energies.append(E) convs.append(C) psi4.set_global_option("FRAC_START", 2) psi4.set_global_option("FRAC_LOAD", True) psi4.set_global_option("GUESS", "READ") psi4.set_global_option("FRAC_DIIS", frac_diis) psi4.set_global_option("DF_INTS_IO", "LOAD") psi4.set_global_option("DF_INTS_IO", old_df_ints_io) # => Print the results out <= # E = {} psi4.print_out( """\n ==> Fractional Occupation Traverse Results <==\n\n""") psi4.print_out("""\t%-11s %-24s %-24s %11s\n""" % ('N', 'Energy', 'H**O Energy', 'Converged')) for k in range(len(occs)): psi4.print_out("""\t%11.3E %24.16E %24.16E %11d\n""" % (occs[k], energies[k], potentials[k], convs[k])) E[occs[k]] = energies[k] psi4.print_out('\n\t"You trying to be a hero Watkins?"\n') psi4.print_out('\t"Just trying to kill some bugs sir!"\n') psi4.print_out('\t\t\t-Starship Troopers\n') # Drop the files out fh = open(traverse_filename, 'w') fh.write("""\t%-11s %-24s %-24s %11s\n""" % ('N', 'Energy', 'H**O Energy', 'Converged')) for k in range(len(occs)): fh.write("""\t%11.3E %24.16E %24.16E %11d\n""" % (occs[k], energies[k], potentials[k], convs[k])) fh.close() # Properly, should clone molecule but since not returned and easy to unblemish, molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) return E
def run_roa(name, **kwargs): # Get list of omega values -> Make sure we only have one wavelength # Catch this now before any real work gets done omega = psi4.get_option('CCRESPONSE', 'OMEGA') if len(omega) > 2: raise Exception( 'ROA scattering can only be performed for one wavelength.') else: pass psi4.print_out('Running ROA computation. Subdirectories for each ' 'required displaced geometry have been created.\n\n') ### Initialize database db = shelve.open('database', writeback=True) if 'inputs_generated' not in db: initialize_database(db) ### Generate input files if not db['inputs_generated']: generate_inputs(name, db) db['inputs_generated'] = True ### If 'serial' calculation, proceed with subdir execution ### Check job status if db['inputs_generated'] and not db['jobs_complete']: print('Checking status') roa_stat(db) for job, status in db['job_status'].items(): print("{} --> {}".format(job, status)) ### Compute ROA Scattering if db['jobs_complete']: # SAVE this for when multiple wavelengths works # # Get list of omega values # omega = psi4.get_option('CCRESPONSE','OMEGA') # if len(omega) > 1: # units = copy.copy(omega[-1]) # omega.pop() # else: # units = 'atomic' # wavelength = copy.copy(omega[0]) # # Set up units for scatter.cc # if units == 'NM': # wavelength = (psi_c * psi_h * 1*(10**-9))/(wavelength * psi_hartree2J) # if units == 'HZ': # wavelength = wavelength * psi_h / psi_hartree2J # if units == 'EV': # wavelength = wavelength / psi_hartree2ev # if units == 'atomic': # pass # Initialize tensor lists dip_polar_list = [] opt_rot_list = [] dip_quad_polar_list = [] gauge_list = [] make_gauge_list(gauge_list) # Gather data synthesize_dipole_polar(db, dip_polar_list) synthesize_opt_rot(db, opt_rot_list) synthesize_dip_quad_polar(db, dip_quad_polar_list) # Compute Scattering # Run new function (src/bin/ccresponse/scatter.cc) psi4.print_out('Running scatter function') step = psi4.get_local_option('FINDIF', 'DISP_SIZE') for gauge in opt_rot_list: g_idx = opt_rot_list.index(gauge) # print('\n\n----------------------------------------------------------------------') # print('\t%%%%%%%%%% {} %%%%%%%%%%'.format(gauge_list[g_idx])) # print('----------------------------------------------------------------------\n\n') psi4.print_out( '\n\n----------------------------------------------------------------------\n' ) psi4.print_out('\t%%%%%%%%%% {} %%%%%%%%%%\n'.format( gauge_list[g_idx])) psi4.print_out( '----------------------------------------------------------------------\n\n' ) psi4.scatter(step, dip_polar_list, gauge, dip_quad_polar_list) db.close()
def anharmonicity(rvals, energies, mol = None): """Generates spectroscopic constants for a diatomic molecules. Fits a diatomic potential energy curve using either a 5 or 9 point Legendre fit, locates the minimum energy point, and then applies second order vibrational perturbation theory to obtain spectroscopic constants. The r values provided must bracket the minimum energy point, or an error will result. A dictionary with the following keys, which correspond to spectroscopic constants, is returned: :type rvals: list :param rvals: The bond lengths (in Angstrom) for which energies are provided of length either 5 or 9 but must be the same length as the energies array :type energies: list :param energies: The energies (Eh) computed at the bond lengths in the rvals list :returns: (*dict*) Keys: "re", "r0", "we", "wexe", "nu", "ZPVE(harmonic)", "ZPVE(anharmonic)", "Be", "B0", "ae", "De" corresponding to the spectroscopic constants in cm-1 """ angstrom_to_bohr = 1.0 / p4const.psi_bohr2angstroms angstrom_to_meter = 10e-10; if len(rvals) != len(energies): raise Exception("The number of energies must match the number of distances") npoints = len(rvals) if npoints != 5 and npoints != 9: raise Exception("Only 5- or 9-point fits are implemented right now") psi4.print_out("\n\nPerforming a %d-point fit\n" % npoints) psi4.print_out("\nOptimizing geometry based on current surface:\n\n"); if (npoints == 5): optx = rvals[2] elif (npoints == 9): optx = rvals[4] # Molecule can be passed in be user. Look at the function definition above. if mol == None: mol = psi4.get_active_molecule() natoms = mol.natom() if natoms != 2: raise Exception("The current molecule must be a diatomic for this code to work!") m1 = mol.mass(0) m2 = mol.mass(1) maxit = 30 thres = 1.0e-9 for i in range(maxit): if (npoints == 5): grad= first_deriv_5pt(rvals, energies, optx) secd = second_deriv_5pt(rvals, energies, optx) energy = function_5pt(rvals, energies, optx) elif (npoints == 9): grad = first_deriv_9pt(rvals, energies, optx) secd = second_deriv_9pt(rvals, energies, optx) energy = function_9pt(rvals, energies, optx) psi4.print_out(" E = %20.14f, x = %14.7f, grad = %20.14f\n" % (energy, optx, grad)) if abs(grad) < thres: break optx -= grad / secd; psi4.print_out(" Final E = %20.14f, x = %14.7f, grad = %20.14f\n" % (function_5pt(rvals, energies, optx), optx, grad)); if optx < min(rvals): raise Exception("Minimum energy point is outside range of points provided. Use a lower range of r values.") if optx > max(rvals): raise Exception("Minimum energy point is outside range of points provided. Use a higher range of r values.") if (npoints == 5): energy = function_5pt(rvals, energies, optx) first = first_deriv_5pt(rvals, energies, optx) secd = second_deriv_5pt(rvals, energies, optx) * p4const.psi_hartree2aJ third = third_deriv_5pt(rvals, energies, optx) * p4const.psi_hartree2aJ fourth = fourth_deriv_5pt(rvals, energies, optx) * p4const.psi_hartree2aJ elif (npoints == 9): energy = function_9pt(rvals, energies, optx) first = first_deriv_9pt(rvals, energies, optx) secd = second_deriv_9pt(rvals, energies, optx) * p4const.psi_hartree2aJ third = third_deriv_9pt(rvals, energies, optx) * p4const.psi_hartree2aJ fourth = fourth_deriv_9pt(rvals, energies, optx) * p4const.psi_hartree2aJ psi4.print_out("\nEquilibrium Energy %20.14f Hartrees\n" % energy) psi4.print_out("Gradient %20.14f\n" % first) psi4.print_out("Quadratic Force Constant %14.7f MDYNE/A\n" % secd) psi4.print_out("Cubic Force Constant %14.7f MDYNE/A**2\n" % third) psi4.print_out("Quartic Force Constant %14.7f MDYNE/A**3\n" % fourth) hbar = p4const.psi_h / (2.0 * pi) mu = ((m1*m2)/(m1+m2))*p4const.psi_amu2kg we = 5.3088375e-11*sqrt(secd/mu) wexe = (1.2415491e-6)*(we/secd)**2 * ((5.0*third*third)/(3.0*secd)-fourth) # Rotational constant: Be I = ((m1*m2)/(m1+m2)) * p4const.psi_amu2kg * (optx * angstrom_to_meter)**2 B = p4const.psi_h / (8.0 * pi**2 * p4const.psi_c * I) # alpha_e and quartic centrifugal distortion constant ae = -(6.0 * B**2 / we) * ((1.05052209e-3*we*third)/(sqrt(B * secd**3))+1.0) de = 4.0*B**3 / we**2 # B0 and r0 (plus re check using Be) B0 = B - ae / 2.0 r0 = sqrt(p4const.psi_h / (8.0 * pi**2 * mu * p4const.psi_c * B0)) recheck = sqrt(p4const.psi_h / (8.0 * pi**2 * mu * p4const.psi_c * B)) r0 /= angstrom_to_meter; recheck /= angstrom_to_meter; # Fundamental frequency nu nu = we - 2.0 * wexe; zpve_nu = 0.5 * we - 0.25 * wexe; psi4.print_out("\nre = %10.6f A check: %10.6f\n" % (optx, recheck)) psi4.print_out("r0 = %10.6f A\n" % r0) psi4.print_out("we = %10.4f cm-1\n" % we) psi4.print_out("wexe = %10.4f cm-1\n" % wexe) psi4.print_out("nu = %10.4f cm-1\n" % nu) psi4.print_out("ZPVE(nu) = %10.4f cm-1\n" % zpve_nu) psi4.print_out("Be = %10.4f cm-1\n" % B) psi4.print_out("B0 = %10.4f cm-1\n" % B0) psi4.print_out("ae = %10.4f cm-1\n" % ae) psi4.print_out("De = %10.7f cm-1\n" % de) results = { "re" : optx, "r0" : r0, "we" : we, "wexe" : wexe, "nu" : nu, "ZPVE(harmonic)" : zpve_nu, "ZPVE(anharmonic)" : zpve_nu, "Be" : B, "B0" : B0, "ae" : ae, "De" : de } return results
def auto_fragments(**kwargs): r"""Detects fragments if the user does not supply them. Currently only used for the WebMO implementation of SAPT. :returns: :ref:`Molecule<sec:psimod_Molecule>`) |w--w| fragmented molecule. :type molecule: :ref:`molecule <op_py_molecule>` :param molecule: ``h2o`` || etc. The target molecule, if not the last molecule defined. :examples: >>> # [1] replicates with cbs() the simple model chemistry scf/cc-pVDZ: set basis cc-pVDZ energy('scf') >>> molecule mol {\nH 0.0 0.0 0.0\nH 2.0 0.0 0.0\nF 0.0 1.0 0.0\nF 2.0 1.0 0.0\n} >>> print mol.nfragments() # 1 >>> fragmol = auto_fragments() >>> print fragmol.nfragments() # 2 """ # Make sure the molecule the user provided is the active one molecule = kwargs.pop('molecule', psi4.get_active_molecule()) molecule.update_geometry() molname = molecule.name() geom = molecule.save_string_xyz() numatoms = molecule.natom() VdW = [1.2, 1.7, 1.5, 1.55, 1.52, 1.9, 1.85, 1.8] symbol = list(range(numatoms)) X = [0.0] * numatoms Y = [0.0] * numatoms Z = [0.0] * numatoms Queue = [] White = [] Black = [] F = geom.split('\n') for f in range(numatoms): A = F[f + 1].split() symbol[f] = A[0] X[f] = float(A[1]) Y[f] = float(A[2]) Z[f] = float(A[3]) White.append(f) Fragment = [[] for i in range(numatoms)] # stores fragments start = 0 # starts with the first atom in the list Queue.append(start) White.remove(start) frag = 0 while ((len(White) > 0) or (len(Queue) > 0)): # Iterates to the next fragment while (len(Queue) > 0): # BFS within a fragment for u in Queue: # find all nearest Neighbors # (still coloured white) to vertex u for i in White: Distance = math.sqrt((X[i] - X[u]) * (X[i] - X[u]) + (Y[i] - Y[u]) * (Y[i] - Y[u]) + (Z[i] - Z[u]) * (Z[i] - Z[u])) if Distance < _autofragment_convert( u, symbol) + _autofragment_convert(i, symbol): Queue.append(i) # if you find you, put it in the que White.remove( i) # and remove it from the untouched list Queue.remove(u) # remove focus from Queue Black.append(u) Fragment[frag].append(int(u + 1)) # add to group (adding 1 to start # list at one instead of zero) if (len(White) != 0): # cant move White->Queue if no more exist Queue.append(White[0]) White.remove(White[0]) frag += 1 new_geom = """\n""" for i in Fragment[0]: new_geom = new_geom + F[i].lstrip() + """\n""" new_geom = new_geom + """--\n""" for j in Fragment[1]: new_geom = new_geom + F[j].lstrip() + """\n""" new_geom = new_geom + """units angstrom\n""" moleculenew = psi4.Molecule.create_molecule_from_string(new_geom) moleculenew.set_name(molname) moleculenew.update_geometry() moleculenew.print_cluster() psi4.print_out(""" Exiting auto_fragments\n""") return moleculenew
def run_roa(name, **kwargs): """ Main driver for managing Raman Optical activity computations with CC response theory. Uses distributed finite differences approach --> 1. Sets up a database to keep track of running/finished/waiting computations. 2. Generates separate input files for displaced geometries. 3. When all displacements are run, collects the necessary information from each displaced computation, and computes final result. """ # Get list of omega values -> Make sure we only have one wavelength # Catch this now before any real work gets done omega = psi4.get_option('CCRESPONSE', 'OMEGA') if len(omega) > 2: raise Exception('ROA scattering can only be performed for one wavelength.') else: pass psi4.print_out( 'Running ROA computation. Subdirectories for each ' 'required displaced geometry have been created.\n\n') dbno = 0 # Initialize database db = shelve.open('database', writeback=True) # Check if final result is in here # ->if we have already computed roa, back up the dict # ->copy it setting this flag to false and continue if ('roa_computed' in db) and ( db['roa_computed'] ): db2 = shelve.open('.database.bak{}'.format(dbno), writeback=True) dbno += 1 for key,value in db.iteritems(): db2[key]=value db2.close() db['roa_computed'] = False else: db['roa_computed'] = False if 'inputs_generated' not in db: findif_response_utils.initialize_database(db,name,"roa", ["roa_tensor"]) # Generate input files if not db['inputs_generated']: findif_response_utils.generate_inputs(db,name) # handled by helper db['inputs_generated'] = True # Check job status if db['inputs_generated'] and not db['jobs_complete']: print('Checking status') findif_response_utils.stat(db) for job, status in db['job_status'].items(): print("{} --> {}".format(job, status)) # Compute ROA Scattering if db['jobs_complete']: mygauge = psi4.get_option('CCRESPONSE', 'GAUGE') consider_gauge = { 'LENGTH': ['Length Gauge'], 'VELOCITY': ['Modified Velocity Gauge'], 'BOTH': ['Length Gauge', 'Modified Velocity Gauge'] } gauge_list = ["{} Results".format(x) for x in consider_gauge[mygauge]] # Gather data dip_polar_list = findif_response_utils.collect_displaced_matrix_data( db, 'Dipole Polarizability', 3) opt_rot_list = [ x for x in ( findif_response_utils.collect_displaced_matrix_data( db, "Optical Rotation Tensor ({})".format(gauge), 3 ) for gauge in consider_gauge[mygauge] ) ] dip_quad_polar_list = findif_response_utils.collect_displaced_matrix_data( db, "Electric-Dipole/Quadrupole Polarizability", 9) # Compute Scattering # Run new function (src/bin/ccresponse/scatter.cc) psi4.print_out('Running scatter function') step = psi4.get_local_option('FINDIF', 'DISP_SIZE') for g_idx, gauge in enumerate(opt_rot_list): print('\n\n----------------------------------------------------------------------') print('\t%%%%%%%%%% {} %%%%%%%%%%'.format(gauge_list[g_idx])) print('----------------------------------------------------------------------\n\n') psi4.print_out('\n\n----------------------------------------------------------------------\n') psi4.print_out('\t%%%%%%%%%% {} %%%%%%%%%%\n'.format(gauge_list[g_idx])) psi4.print_out('----------------------------------------------------------------------\n\n') print('roa.py:85 I am not being passed a molecule, grabbing from global :(') psi4.scatter(psi4.get_active_molecule(), step, dip_polar_list, gauge, dip_quad_polar_list) db['roa_computed'] = True db.close()
def run_dftd3(self, func=None, dashlvl=None, dashparam=None, dertype=None, verbose=False): """Function to call Grimme's dftd3 program (http://toc.uni-muenster.de/DFTD3/) to compute the -D correction of level *dashlvl* using parameters for the functional *func*. The dictionary *dashparam* can be used to supply a full set of dispersion parameters in the absense of *func* or to supply individual overrides in the presence of *func*. Returns energy if *dertype* is 0, gradient if *dertype* is 1, else tuple of energy and gradient if *dertype* unspecified. The dftd3 executable must be independently compiled and found in :envvar:`PATH` or :envvar:`PSIPATH`. *self* may be either a qcdb.Molecule (sensibly) or a psi4.Molecule (works b/c psi4.Molecule has been extended by this method py-side and only public interface fns used) or a string that can be instantiated into a qcdb.Molecule. """ # Create (if necessary) and update qcdb.Molecule if isinstance(self, Molecule): # called on a qcdb.Molecule pass elif isinstance(self, psi4.Molecule): # called on a python export of a psi4.Molecule (py-side through Psi4's driver) self.create_psi4_string_from_molecule() elif isinstance(self, basestring): # called on a string representation of a psi4.Molecule (c-side through psi4.Dispersion) self = Molecule(self) else: raise ValidationError("""Argument mol must be psi4string or qcdb.Molecule""") self.update_geometry() # Validate arguments dashlvl = dashlvl.lower() dashlvl = dash_alias['-' + dashlvl][1:] if ('-' + dashlvl) in dash_alias.keys() else dashlvl if dashlvl not in dashcoeff.keys(): raise ValidationError("""-D correction level %s is not available. Choose among %s.""" % (dashlvl, dashcoeff.keys())) if dertype is None: dertype = -1 elif der0th.match(str(dertype)): dertype = 0 elif der1st.match(str(dertype)): dertype = 1 elif der2nd.match(str(dertype)): raise ValidationError('Requested derivative level \'dertype\' %s not valid for run_dftd3.' % (dertype)) else: raise ValidationError('Requested derivative level \'dertype\' %s not valid for run_dftd3.' % (dertype)) if func is None: if dashparam is None: # defunct case raise ValidationError("""Parameters for -D correction missing. Provide a func or a dashparam kwarg.""") else: # case where all param read from dashparam dict (which must have all correct keys) func = 'custom' dashcoeff[dashlvl][func] = {} dashparam = dict((k.lower(), v) for k, v in dashparam.iteritems()) for key in dashcoeff[dashlvl]['b3lyp'].keys(): if key in dashparam.keys(): dashcoeff[dashlvl][func][key] = dashparam[key] else: raise ValidationError("""Parameter %s is missing from dashparam dict %s.""" % (key, dashparam)) else: func = func.lower() if func not in dashcoeff[dashlvl].keys(): raise ValidationError("""Functional %s is not available for -D level %s.""" % (func, dashlvl)) if dashparam is None: # (normal) case where all param taken from dashcoeff above pass else: # case where items in dashparam dict can override param taken from dashcoeff above dashparam = dict((k.lower(), v) for k, v in dashparam.iteritems()) for key in dashcoeff[dashlvl]['b3lyp'].keys(): if key in dashparam.keys(): dashcoeff[dashlvl][func][key] = dashparam[key] # Move ~/.dftd3par.<hostname> out of the way so it won't interfere defaultfile = os.path.expanduser('~') + '/.dftd3par.' + socket.gethostname() defmoved = False if os.path.isfile(defaultfile): os.rename(defaultfile, defaultfile + '_hide') defmoved = True # Find environment by merging PSIPATH and PATH environment variables lenv = { 'PATH': ':'.join([os.path.abspath(x) for x in os.environ.get('PSIPATH', '').split(':') if x != '']) + \ ':' + os.environ.get('PATH'), 'LD_LIBRARY_PATH': os.environ.get('LD_LIBRARY_PATH') } # Find out if running from Psi4 for scratch details and such try: psi4.version() except NameError: isP4regime = False else: isP4regime = True # Setup unique scratch directory and move in current_directory = os.getcwd() if isP4regime: psioh = psi4.IOManager.shared_object() psio = psi4.IO.shared_object() os.chdir(psioh.get_default_path()) dftd3_tmpdir = 'psi.' + str(os.getpid()) + '.' + psio.get_default_namespace() + \ '.dftd3.' + str(random.randint(0, 99999)) else: dftd3_tmpdir = os.environ['HOME'] + os.sep + 'dftd3_' + str(random.randint(0, 99999)) if os.path.exists(dftd3_tmpdir) is False: os.mkdir(dftd3_tmpdir) os.chdir(dftd3_tmpdir) # Write dftd3_parameters file that governs dispersion calc paramcontents = dash_server(func, dashlvl, 'dftd3') paramfile1 = 'dftd3_parameters' # older patched name with open(paramfile1, 'w') as handle: handle.write(paramcontents) paramfile2 = '.dftd3par.local' # new mainline name with open(paramfile2, 'w') as handle: handle.write(paramcontents) # Write dftd3_geometry file that supplies geometry to dispersion calc numAtoms = self.natom() geom = self.save_string_xyz() reals = [] for line in geom.splitlines(): lline = line.split() if len(lline) != 4: continue if lline[0] == 'Gh': numAtoms -= 1 else: reals.append(line) geomtext = str(numAtoms) + '\n\n' for line in reals: geomtext += line.strip() + '\n' geomfile = './dftd3_geometry.xyz' with open(geomfile, 'w') as handle: handle.write(geomtext) # TODO somehow the variations on save_string_xyz and # whether natom and chgmult does or doesn't get written # have gotten all tangled. I fear this doesn't work # the same btwn libmints and qcdb or for ghosts # Call dftd3 program command = ['dftd3', geomfile] if dertype != 0: command.append('-grad') try: dashout = subprocess.Popen(command, stdout=subprocess.PIPE, env=lenv) except OSError as e: raise ValidationError('Program dftd3 not found in path. %s' % e) out, err = dashout.communicate() # Parse output (could go further and break into E6, E8, E10 and Cn coeff) success = False for line in out.splitlines(): if re.match(' Edisp /kcal,au', line): sline = line.split() dashd = float(sline[3]) if re.match(' normal termination of dftd3', line): success = True if not success: os.chdir(current_directory) raise Dftd3Error("""Unsuccessful run. Possibly -D variant not available in dftd3 version.""") # Parse grad output if dertype != 0: derivfile = './dftd3_gradient' dfile = open(derivfile, 'r') dashdderiv = [] for line in geom.splitlines(): lline = line.split() if len(lline) != 4: continue if lline[0] == 'Gh': dashdderiv.append([0.0, 0.0, 0.0]) else: dashdderiv.append([float(x.replace('D', 'E')) for x in dfile.readline().split()]) dfile.close() if len(dashdderiv) != self.natom(): raise ValidationError('Program dftd3 gradient file has %d atoms- %d expected.' % \ (len(dashdderiv), self.natom())) # Prepare results for Psi4 if isP4regime and dertype != 0: psi4.set_variable('DISPERSION CORRECTION ENERGY', dashd) psi_dashdderiv = psi4.Matrix(self.natom(), 3) psi_dashdderiv.set(dashdderiv) # Print program output to file if verbose if isP4regime: verbose = True if psi4.get_option('SCF', 'PRINT') >= 3 else False if verbose: text = '\n ==> DFTD3 Output <==\n' text += out if dertype != 0: with open(derivfile, 'r') as handle: text += handle.read().replace('D', 'E') text += '\n' if isP4regime: psi4.print_out(text) else: print(text) # Clean up files and remove scratch directory os.unlink(paramfile1) os.unlink(paramfile2) os.unlink(geomfile) if dertype != 0: os.unlink(derivfile) if defmoved is True: os.rename(defaultfile + '_hide', defaultfile) os.chdir('..') try: shutil.rmtree(dftd3_tmpdir) except OSError as e: ValidationError('Unable to remove dftd3 temporary directory %s' % e) os.chdir(current_directory) # return -D & d(-D)/dx if dertype == -1: return dashd, dashdderiv elif dertype == 0: return dashd elif dertype == 1: return psi_dashdderiv
def run_dftd3(self, func=None, dashlvl=None, dashparam=None, dertype=None, verbose=False): """Function to call Grimme's dftd3 program (http://toc.uni-muenster.de/DFTD3/) to compute the -D correction of level *dashlvl* using parameters for the functional *func*. The dictionary *dashparam* can be used to supply a full set of dispersion parameters in the absense of *func* or to supply individual overrides in the presence of *func*. Returns energy if *dertype* is 0, gradient if *dertype* is 1, else tuple of energy and gradient if *dertype* unspecified. The dftd3 executable must be independently compiled and found in :envvar:`PATH` or :envvar:`PSIPATH`. *self* may be either a qcdb.Molecule (sensibly) or a psi4.Molecule (works b/c psi4.Molecule has been extended by this method py-side and only public interface fns used) or a string that can be instantiated into a qcdb.Molecule. """ # Create (if necessary) and update qcdb.Molecule if isinstance(self, Molecule): # called on a qcdb.Molecule pass elif isinstance(self, psi4.Molecule): # called on a python export of a psi4.Molecule (py-side through Psi4's driver) self.create_psi4_string_from_molecule() elif isinstance(self, basestring): # called on a string representation of a psi4.Molecule (c-side through psi4.Dispersion) self = Molecule(self) else: raise ValidationError( """Argument mol must be psi4string or qcdb.Molecule""") self.update_geometry() # Validate arguments dashlvl = dashlvl.lower() dashlvl = dash_alias['-' + dashlvl][1:] if ( '-' + dashlvl) in dash_alias.keys() else dashlvl if dashlvl not in dashcoeff.keys(): raise ValidationError( """-D correction level %s is not available. Choose among %s.""" % (dashlvl, dashcoeff.keys())) if dertype is None: dertype = -1 elif der0th.match(str(dertype)): dertype = 0 elif der1st.match(str(dertype)): dertype = 1 elif der2nd.match(str(dertype)): raise ValidationError( 'Requested derivative level \'dertype\' %s not valid for run_dftd3.' % (dertype)) else: raise ValidationError( 'Requested derivative level \'dertype\' %s not valid for run_dftd3.' % (dertype)) if func is None: if dashparam is None: # defunct case raise ValidationError( """Parameters for -D correction missing. Provide a func or a dashparam kwarg.""" ) else: # case where all param read from dashparam dict (which must have all correct keys) func = 'custom' dashcoeff[dashlvl][func] = {} dashparam = dict((k.lower(), v) for k, v in dashparam.iteritems()) for key in dashcoeff[dashlvl]['b3lyp'].keys(): if key in dashparam.keys(): dashcoeff[dashlvl][func][key] = dashparam[key] else: raise ValidationError( """Parameter %s is missing from dashparam dict %s.""" % (key, dashparam)) else: func = func.lower() if func not in dashcoeff[dashlvl].keys(): raise ValidationError( """Functional %s is not available for -D level %s.""" % (func, dashlvl)) if dashparam is None: # (normal) case where all param taken from dashcoeff above pass else: # case where items in dashparam dict can override param taken from dashcoeff above dashparam = dict((k.lower(), v) for k, v in dashparam.iteritems()) for key in dashcoeff[dashlvl]['b3lyp'].keys(): if key in dashparam.keys(): dashcoeff[dashlvl][func][key] = dashparam[key] # Move ~/.dftd3par.<hostname> out of the way so it won't interfere defaultfile = os.path.expanduser( '~') + '/.dftd3par.' + socket.gethostname() defmoved = False if os.path.isfile(defaultfile): os.rename(defaultfile, defaultfile + '_hide') defmoved = True # Find environment by merging PSIPATH and PATH environment variables lenv = { 'PATH': ':'.join([os.path.abspath(x) for x in os.environ.get('PSIPATH', '').split(':') if x != '']) + \ ':' + os.environ.get('PATH'), 'LD_LIBRARY_PATH': os.environ.get('LD_LIBRARY_PATH') } # Filter out None values as subprocess will fault on them lenv = {k: v for k, v in lenv.items() if v is not None} # Find out if running from Psi4 for scratch details and such try: psi4.version() except NameError: isP4regime = False else: isP4regime = True # Setup unique scratch directory and move in current_directory = os.getcwd() if isP4regime: psioh = psi4.IOManager.shared_object() psio = psi4.IO.shared_object() os.chdir(psioh.get_default_path()) dftd3_tmpdir = 'psi.' + str(os.getpid()) + '.' + psio.get_default_namespace() + \ '.dftd3.' + str(random.randint(0, 99999)) else: dftd3_tmpdir = os.environ['HOME'] + os.sep + 'dftd3_' + str( random.randint(0, 99999)) if os.path.exists(dftd3_tmpdir) is False: os.mkdir(dftd3_tmpdir) os.chdir(dftd3_tmpdir) # Write dftd3_parameters file that governs dispersion calc paramcontents = dash_server(func, dashlvl, 'dftd3') paramfile1 = 'dftd3_parameters' # older patched name with open(paramfile1, 'w') as handle: handle.write(paramcontents) paramfile2 = '.dftd3par.local' # new mainline name with open(paramfile2, 'w') as handle: handle.write(paramcontents) # Write dftd3_geometry file that supplies geometry to dispersion calc numAtoms = self.natom() geom = self.save_string_xyz() reals = [] for line in geom.splitlines(): lline = line.split() if len(lline) != 4: continue if lline[0] == 'Gh': numAtoms -= 1 else: reals.append(line) geomtext = str(numAtoms) + '\n\n' for line in reals: geomtext += line.strip() + '\n' geomfile = './dftd3_geometry.xyz' with open(geomfile, 'w') as handle: handle.write(geomtext) # TODO somehow the variations on save_string_xyz and # whether natom and chgmult does or doesn't get written # have gotten all tangled. I fear this doesn't work # the same btwn libmints and qcdb or for ghosts # Call dftd3 program command = ['dftd3', geomfile] if dertype != 0: command.append('-grad') try: dashout = subprocess.Popen(command, stdout=subprocess.PIPE, env=lenv) except OSError as e: raise ValidationError('Program dftd3 not found in path. %s' % e) out, err = dashout.communicate() # Parse output (could go further and break into E6, E8, E10 and Cn coeff) success = False for line in out.splitlines(): if re.match(' Edisp /kcal,au', line): sline = line.split() dashd = float(sline[3]) if re.match(' normal termination of dftd3', line): success = True if not success: os.chdir(current_directory) raise Dftd3Error( """Unsuccessful run. Possibly -D variant not available in dftd3 version.""" ) # Parse grad output if dertype != 0: derivfile = './dftd3_gradient' dfile = open(derivfile, 'r') dashdderiv = [] for line in geom.splitlines(): lline = line.split() if len(lline) != 4: continue if lline[0] == 'Gh': dashdderiv.append([0.0, 0.0, 0.0]) else: dashdderiv.append([ float(x.replace('D', 'E')) for x in dfile.readline().split() ]) dfile.close() if len(dashdderiv) != self.natom(): raise ValidationError('Program dftd3 gradient file has %d atoms- %d expected.' % \ (len(dashdderiv), self.natom())) # Prepare results for Psi4 if isP4regime and dertype != 0: psi4.set_variable('DISPERSION CORRECTION ENERGY', dashd) psi_dashdderiv = psi4.Matrix(self.natom(), 3) psi_dashdderiv.set(dashdderiv) # Print program output to file if verbose if isP4regime: verbose = True if psi4.get_option('SCF', 'PRINT') >= 3 else False if verbose: text = '\n ==> DFTD3 Output <==\n' text += out if dertype != 0: with open(derivfile, 'r') as handle: text += handle.read().replace('D', 'E') text += '\n' if isP4regime: psi4.print_out(text) else: print(text) # Clean up files and remove scratch directory os.unlink(paramfile1) os.unlink(paramfile2) os.unlink(geomfile) if dertype != 0: os.unlink(derivfile) if defmoved is True: os.rename(defaultfile + '_hide', defaultfile) os.chdir('..') try: shutil.rmtree(dftd3_tmpdir) except OSError as e: ValidationError('Unable to remove dftd3 temporary directory %s' % e) os.chdir(current_directory) # return -D & d(-D)/dx if dertype == -1: return dashd, dashdderiv elif dertype == 0: return dashd elif dertype == 1: return psi_dashdderiv
def run_dftd3(self, func=None, dashlvl=None, dashparam=None, dertype=None): """Function to call Grimme's dftd3 program (http://toc.uni-muenster.de/DFTD3/) to compute the -D correction of level *dashlvl* using parameters for the functional *func*. The dictionary *dashparam* can be used to supply a full set of dispersion parameters in the absense of *func* or to supply individual overrides in the presence of *func*. Returns energy if *dertype* is 0, gradient if *dertype* is 1, else tuple of energy and gradient if *dertype* unspecified. The dftd3 executable must be independently compiled and found in :envvar:`PATH`. """ # Validate arguments if self is None: self = psi4.get_active_molecule() dashlvl = dashlvl.lower() dashlvl = dash_alias['-' + dashlvl][1:] if ('-' + dashlvl) in dash_alias.keys() else dashlvl if dashlvl not in dashcoeff.keys(): raise ValidationError("""-D correction level %s is not available. Choose among %s.""" % (dashlvl, dashcoeff.keys())) if dertype is None: dertype = -1 elif der0th.match(str(dertype)): dertype = 0 elif der1st.match(str(dertype)): dertype = 1 elif der2nd.match(str(dertype)): raise ValidationError('Requested derivative level \'dertype\' %s not valid for run_dftd3.' % (dertype)) else: raise ValidationError('Requested derivative level \'dertype\' %s not valid for run_dftd3.' % (dertype)) if func is None: if dashparam is None: # defunct case raise ValidationError("""Parameters for -D correction missing. Provide a func or a dashparam kwarg.""") else: # case where all param read from dashparam dict (which must have all correct keys) func = 'custom' dashcoeff[dashlvl][func] = {} dashparam = dict((k.lower(), v) for k, v in dashparam.items()) for key in dashcoeff[dashlvl]['b3lyp'].keys(): if key in dashparam.keys(): dashcoeff[dashlvl][func][key] = dashparam[key] else: raise ValidationError("""Parameter %s is missing from dashparam dict %s.""" % (key, dashparam)) else: func = func.lower() if func not in dashcoeff[dashlvl].keys(): raise ValidationError("""Functional %s is not available for -D level %s.""" % (func, dashlvl)) if dashparam is None: # (normal) case where all param taken from dashcoeff above pass else: # case where items in dashparam dict can override param taken from dashcoeff above dashparam = dict((k.lower(), v) for k, v in dashparam.items()) for key in dashcoeff[dashlvl]['b3lyp'].keys(): if key in dashparam.keys(): dashcoeff[dashlvl][func][key] = dashparam[key] # Move ~/.dftd3par.<hostname> out of the way so it won't interfere defaultfile = os.path.expanduser('~') + '/.dftd3par.' + socket.gethostname() defmoved = False if os.path.isfile(defaultfile): os.rename(defaultfile, defaultfile + '_hide') defmoved = True # Setup unique scratch directory and move in current_directory = os.getcwd() psioh = psi4.IOManager.shared_object() psio = psi4.IO.shared_object() os.chdir(psioh.get_default_path()) dftd3_tmpdir = 'psi.' + str(os.getpid()) + '.' + psio.get_default_namespace() + \ '.dftd3.' + str(random.randint(0, 99999)) if os.path.exists(dftd3_tmpdir) is False: os.mkdir(dftd3_tmpdir) os.chdir(dftd3_tmpdir) # Write dftd3_parameters file that governs dispersion calc paramfile = './dftd3_parameters' pfile = open(paramfile, 'w') pfile.write(dash_server(func, dashlvl, 'dftd3')) pfile.close() # Write dftd3_geometry file that supplies geometry to dispersion calc geomfile = './dftd3_geometry.xyz' gfile = open(geomfile, 'w') numAtoms = self.natom() geom = self.save_string_xyz() reals = [] for line in geom.splitlines(): if line.split()[0] == 'Gh': numAtoms -= 1 else: reals.append(line) gfile.write(str(numAtoms)+'\n') for line in reals: gfile.write(line.strip()+'\n') gfile.close() # Call dftd3 program try: dashout = subprocess.Popen(['dftd3', geomfile, '-grad'], stdout=subprocess.PIPE) except OSError: raise ValidationError('Program dftd3 not found in path.') out, err = dashout.communicate() # Parse output (could go further and break into E6, E8, E10 and Cn coeff) success = False for line in out.splitlines(): # communicate in python 3 returns a byte array rather than a string. # Convert to string--only need a simple encoding for comparison. line = line.decode('utf-8') if re.match(' Edisp /kcal,au', line): sline = line.split() dashd = float(sline[3]) if re.match(' normal termination of dftd3', line): success = True if not success: raise ValidationError('Program dftd3 did not complete successfully.') # Parse grad output derivfile = './dftd3_gradient' dfile = open(derivfile, 'r') dashdderiv = [] i = 0 for line in geom.splitlines(): if i == 0: i += 1 else: if line.split()[0] == 'Gh': dashdderiv.append([0.0, 0.0, 0.0]) else: temp = dfile.readline() dashdderiv.append([float(x.replace('D', 'E')) for x in temp.split()]) dfile.close() if len(dashdderiv) != self.natom(): raise ValidationError('Program dftd3 gradient file has %d atoms- %d expected.' % \ (len(dashdderiv), self.natom())) psi_dashdderiv = psi4.Matrix(self.natom(), 3) psi_dashdderiv.set(dashdderiv) # Print program output to file if verbose verbose = psi4.get_option('SCF', 'PRINT') if verbose >= 3: psi4.print_out('\n ==> DFTD3 Output <==\n') psi4.print_out(out) dfile = open(derivfile, 'r') psi4.print_out(dfile.read().replace('D', 'E')) dfile.close() psi4.print_out('\n') # Clean up files and remove scratch directory os.unlink(paramfile) os.unlink(geomfile) os.unlink(derivfile) if defmoved is True: os.rename(defaultfile + '_hide', defaultfile) os.chdir('..') try: shutil.rmtree(dftd3_tmpdir) except OSError as e: ValidationError('Unable to remove dftd3 temporary directory %s' % e, file=sys.stderr) os.chdir(current_directory) # return -D & d(-D)/dx psi4.set_variable('DISPERSION CORRECTION ENERGY', dashd) if dertype == -1: return dashd, dashdderiv elif dertype == 0: return dashd elif dertype == 1: return psi_dashdderiv
def frac_nuke(molecule, **kwargs): kwargs = p4util.kwargs_lower(kwargs) # The molecule is required, and should be the neutral species molecule.update_geometry() charge0 = molecule.molecular_charge() mult0 = molecule.multiplicity() # By default, we start the frac procedure on the 25th iteration # when not reading a previous guess frac_start = kwargs.get('frac_start', 25) # By default, we occupy by tenths of electrons foccs = kwargs.get('foccs', [1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0]) # By default, H**O and LUMO are both in alpha N = 0; for A in range(molecule.natom()): N += molecule.Z(A) N -= charge0 N = int(N) Nb = int((N - mult0 + 1) / 2) Na = int(N - Nb) charge = charge0 mult = mult0 # By default, nuke all the electrons Nmin = 0; if ('nmax' in kwargs): Nmin = N - int(kwargs['nmax']) # By default, DIIS in FRAC (1.0 occupation is always DIIS'd) frac_diis = kwargs.get('frac_diis', True) # By default, drop the files to the molecule's name root = kwargs.get('filename', molecule.name()) traverse_filename = root + '.traverse.dat' stats_filename = root + '.stats.dat' # => Traverse <= # psi4.set_global_option("DF_INTS_IO", "SAVE") Ns = [] energies = [] potentials = [] convs = [] stats = [] # Run one SCF to burn things in E, wfn= energy('scf', return_wfn=True, molecule=molecule, **kwargs) # Determine H**O eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() if Na == Nb: H**O = -Nb elif Nb == 0: H**O = Na else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if E_a >= E_b: H**O = Na else: H**O = -Nb stats.append("""\t%6d %6d %6d %6d %6d %6d\n""" % (N, Na, Nb, charge, mult, H**O)) if H**O > 0: Na = Na - 1 else: Nb = Nb - 1 charge = charge + 1 mult = Na - Nb + 1 psi4.set_global_option("DF_INTS_IO", "LOAD") psi4.set_global_option("FRAC_START", frac_start) psi4.set_global_option("FRAC_RENORMALIZE", True) # Nuke 'em Rico! for Nintegral in range(N, Nmin, -1): # Nuke the current H**O for occ in foccs: psi4.set_global_option("FRAC_OCC", [H**O]) psi4.set_global_option("FRAC_VAL", [occ]) E, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) C = 1 if E == 0.0: E = psi4.get_variable('SCF ITERATION ENERGY') C = 0 if H**O > 0: eps = wfn.epsilon_a() potentials.append(eps[H**O - 1]) else: eps = wfn.epsilon_b() potentials.append(eps[-H**O - 1]) Ns.append(Nintegral + occ - 1.0) energies.append(E) convs.append(C) psi4.set_global_option("FRAC_START", 2) psi4.set_global_option("FRAC_LOAD", True) psi4.set_global_option("FRAC_DIIS", frac_diis) psi4.set_global_option("GUESS", "READ") # Set the next charge/mult molecule.set_molecular_charge(charge) molecule.set_multiplicity(mult) # Determine H**O print('DGAS: What ref should this point to?') #ref = psi4.legacy_wavefunction() eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() if Na == Nb: H**O = -Nb elif Nb == 0: H**O = Na else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if E_a >= E_b: H**O = Na else: H**O = -Nb stats.append("""\t%6d %6d %6d %6d %6d %6d\n""" % (Nintegral-1, Na, Nb, charge, mult, H**O)) if H**O > 0: Na = Na - 1 else: Nb = Nb - 1 charge = charge + 1 mult = Na - Nb + 1 psi4.set_global_option("DF_INTS_IO", "NONE") # => Print the results out <= # E = {} psi4.print_out("""\n ==> Fractional Occupation Nuke Results <==\n\n""") psi4.print_out("""\t%-11s %-24s %-24s %11s\n""" % ('N', 'Energy', 'H**O Energy', 'Converged')) for k in range(len(Ns)): psi4.print_out("""\t%11.3E %24.16E %24.16E %11d\n""" % (Ns[k], energies[k], potentials[k], convs[k])) E[Ns[k]] = energies[k] psi4.print_out('\n') psi4.print_out("""\t%6s %6s %6s %6s %6s %6s\n""" % ('N', 'Na', 'Nb', 'Charge', 'Mult', 'H**O')) for line in stats: psi4.print_out(line) psi4.print_out('\n\t"You shoot a nuke down a bug hole, you got a lot of dead bugs"\n') psi4.print_out('\t\t\t-Starship Troopers\n') # Drop the files out fh = open(traverse_filename, 'w') fh.write("""\t%-11s %-24s %-24s %11s\n""" % ('N', 'Energy', 'H**O Energy', 'Converged')) for k in range(len(Ns)): fh.write("""\t%11.3E %24.16E %24.16E %11d\n""" % (Ns[k], energies[k], potentials[k], convs[k])) fh.close() fh = open(stats_filename, 'w') fh.write("""\t%6s %6s %6s %6s %6s %6s\n""" % ('N', 'Na', 'Nb', 'Charge', 'Mult', 'H**O')) for line in stats: fh.write(line) fh.close() # Properly, should clone molecule but since not returned and easy to unblemish, molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) return E
def _nbody_gufunc(func, method_string, **kwargs): """ Computes the nbody interaction energy, gradient, or Hessian depending on input. Parameters ---------- func : python function Python function that accepts method_string and a molecule and returns a energy, gradient, or Hessian. method_string : str Lowername to be passed to function molecule : psi4.Molecule (default: Global Molecule) Molecule to use in all computations return_wfn : bool (default: False) Return a wavefunction or not bsse_type : str or list (default: None, this function is not called) Type of BSSE correction to compute: CP, NoCP, or VMFC. The first in this list is returned by this function. max_nbody : int Maximum n-body to compute, cannot exceede the number of fragments in the moleucle ptype : str Type of the procedure passed in return_total_data : bool (default: False) If True returns the total data (energy/gradient/etc) of the system otherwise returns interaction data Returns ------- data : return type of func The interaction data wfn : psi4.Wavefunction (optional) A wavefunction with energy/gradient/hessian set appropriotely. This wavefunction also contains Notes ----- This is a generalized univeral function for compute interaction quantities. Examples -------- """ ### ==> Parse some kwargs <== kwargs = p4util.kwargs_lower(kwargs) return_wfn = kwargs.pop('return_wfn', False) ptype = kwargs.pop('ptype', None) return_total_data = kwargs.pop('return_total_data', False) molecule = kwargs.pop('molecule', psi4.get_active_molecule()) molecule.update_geometry() psi4.clean_variables() if ptype not in ['energy', 'gradient', 'hessian']: raise ValidationError( """N-Body driver: The ptype '%s' is not regonized.""" % ptype) # Figure out BSSE types do_cp = False do_nocp = False do_vmfc = False return_method = False # Must be passed bsse_type bsse_type_list = kwargs.pop('bsse_type') if bsse_type_list is None: raise ValidationError("N-Body GUFunc: Must pass a bsse_type") if not isinstance(bsse_type_list, list): bsse_type_list = [bsse_type_list] for num, btype in enumerate(bsse_type_list): if btype.lower() == 'cp': do_cp = True if (num == 0): return_method = 'cp' elif btype.lower() == 'nocp': do_nocp = True if (num == 0): return_method = 'nocp' elif btype.lower() == 'vmfc': do_vmfc = True if (num == 0): return_method = 'vmfc' else: raise ValidationError( "N-Body GUFunc: bsse_type '%s' is not recognized" % btype.lower()) max_nbody = kwargs.get('max_nbody', -1) max_frag = molecule.nfragments() if max_nbody == -1: max_nbody = molecule.nfragments() else: max_nbody = min(max_nbody, max_frag) # What levels do we need? nbody_range = range(1, max_nbody + 1) fragment_range = range(1, max_frag + 1) # Flip this off for now, needs more testing # If we are doing CP lets save them integrals #if 'cp' in bsse_type_list and (len(bsse_type_list) == 1): # # Set to save RI integrals for repeated full-basis computations # ri_ints_io = psi4.get_global_option('DF_INTS_IO') # # inquire if above at all applies to dfmp2 or just scf # psi4.set_global_option('DF_INTS_IO', 'SAVE') # psioh = psi4.IOManager.shared_object() # psioh.set_specific_retention(97, True) bsse_str = bsse_type_list[0] if len(bsse_type_list) > 1: bsse_str = str(bsse_type_list) psi4.print_out("\n\n") psi4.print_out(" ===> N-Body Interaction Abacus <===\n") psi4.print_out(" BSSE Treatment: %s\n" % bsse_str) cp_compute_list = {x: set() for x in nbody_range} nocp_compute_list = {x: set() for x in nbody_range} vmfc_compute_list = {x: set() for x in nbody_range} vmfc_level_list = {x: set() for x in nbody_range } # Need to sum something slightly different # Build up compute sets if do_cp: # Everything is in dimer basis basis_tuple = tuple(fragment_range) for nbody in nbody_range: for x in it.combinations(fragment_range, nbody): cp_compute_list[nbody].add((x, basis_tuple)) if do_nocp: # Everything in monomer basis for nbody in nbody_range: for x in it.combinations(fragment_range, nbody): nocp_compute_list[nbody].add((x, x)) if do_vmfc: # Like a CP for all combinations of pairs or greater for nbody in nbody_range: for cp_combos in it.combinations(fragment_range, nbody): basis_tuple = tuple(cp_combos) for interior_nbody in nbody_range: for x in it.combinations(cp_combos, interior_nbody): combo_tuple = (x, basis_tuple) vmfc_compute_list[interior_nbody].add(combo_tuple) vmfc_level_list[len(basis_tuple)].add(combo_tuple) # Build a comprehensive compute_range compute_list = {x: set() for x in nbody_range} for n in nbody_range: compute_list[n] |= cp_compute_list[n] compute_list[n] |= nocp_compute_list[n] compute_list[n] |= vmfc_compute_list[n] psi4.print_out(" Number of %d-body computations: %d\n" % (n, len(compute_list[n]))) # Build size and slices dictionaries fragment_size_dict = { frag: molecule.extract_subsets(frag).natom() for frag in range(1, max_frag + 1) } start = 0 fragment_slice_dict = {} for k, v in fragment_size_dict.items(): fragment_slice_dict[k] = slice(start, start + v) start += v molecule_total_atoms = sum(fragment_size_dict.values()) # Now compute the energies energies_dict = {} ptype_dict = {} for n in compute_list.keys(): psi4.print_out( "\n ==> N-Body: Now computing %d-body complexes <==\n\n" % n) print("\n ==> N-Body: Now computing %d-body complexes <==\n" % n) total = len(compute_list[n]) for num, pair in enumerate(compute_list[n]): psi4.print_out( "\n N-Body: Computing complex (%d/%d) with fragments %s in the basis of fragments %s.\n\n" % (num + 1, total, str(pair[0]), str(pair[1]))) ghost = list(set(pair[1]) - set(pair[0])) current_mol = molecule.extract_subsets(list(pair[0]), ghost) ptype_dict[pair] = func(method_string, molecule=current_mol, **kwargs) energies_dict[pair] = psi4.get_variable("CURRENT ENERGY") psi4.print_out( "\n N-Body: Complex Energy (fragments = %s, basis = %s: %20.14f)\n" % (str(pair[0]), str(pair[1]), energies_dict[pair])) # Flip this off for now, needs more testing #if 'cp' in bsse_type_list and (len(bsse_type_list) == 1): # psi4.set_global_option('DF_INTS_IO', 'LOAD') psi4.clean() # Final dictionaries cp_energy_by_level = {n: 0.0 for n in nbody_range} nocp_energy_by_level = {n: 0.0 for n in nbody_range} cp_energy_body_dict = {n: 0.0 for n in nbody_range} nocp_energy_body_dict = {n: 0.0 for n in nbody_range} vmfc_energy_body_dict = {n: 0.0 for n in nbody_range} # Build out ptype dictionaries if needed if ptype != 'energy': if ptype == 'gradient': arr_shape = (molecule_total_atoms, 3) elif ptype == 'hessian': arr_shape = (molecule_total_atoms * 3, molecule_total_atoms * 3) else: raise KeyError("N-Body: ptype '%s' not recognized" % ptype) cp_ptype_by_level = {n: np.zeros(arr_shape) for n in nbody_range} nocp_ptype_by_level = {n: np.zeros(arr_shape) for n in nbody_range} cp_ptype_body_dict = {n: np.zeros(arr_shape) for n in nbody_range} nocp_ptype_body_dict = {n: np.zeros(arr_shape) for n in nbody_range} vmfc_ptype_body_dict = {n: np.zeros(arr_shape) for n in nbody_range} else: cp_ptype_by_level, cp_ptype_body_dict = None, None nocp_ptype_by_level, nocp_ptype_body_dict = None, None vmfc_ptype_body_dict = None # Sum up all of the levels for n in nbody_range: # Energy cp_energy_by_level[n] = sum(energies_dict[v] for v in cp_compute_list[n]) nocp_energy_by_level[n] = sum(energies_dict[v] for v in nocp_compute_list[n]) # Special vmfc case if n > 1: vmfc_energy_body_dict[n] = vmfc_energy_body_dict[n - 1] for tup in vmfc_level_list[n]: vmfc_energy_body_dict[n] += ( (-1)**(n - len(tup[0]))) * energies_dict[tup] # Do ptype if ptype != 'energy': _sum_cluster_ptype_data(ptype, ptype_dict, cp_compute_list[n], fragment_slice_dict, fragment_size_dict, cp_ptype_by_level[n]) _sum_cluster_ptype_data(ptype, ptype_dict, nocp_compute_list[n], fragment_slice_dict, fragment_size_dict, nocp_ptype_by_level[n]) _sum_cluster_ptype_data(ptype, ptype_dict, vmfc_level_list[n], fragment_slice_dict, fragment_size_dict, vmfc_ptype_by_level[n], vmfc=True) # Compute cp energy and ptype if do_cp: for n in nbody_range: if n == max_frag: cp_energy_body_dict[n] = cp_energy_by_level[n] if ptype != 'energy': cp_ptype_body_dict[n][:] = cp_ptype_by_level[n] continue for k in range(1, n + 1): take_nk = nCr(max_frag - k - 1, n - k) sign = ((-1)**(n - k)) value = cp_energy_by_level[k] cp_energy_body_dict[n] += take_nk * sign * value if ptype != 'energy': value = cp_ptype_by_level[k] cp_ptype_body_dict[n] += take_nk * sign * value _print_nbody_energy(cp_energy_body_dict, "Counterpoise Corrected (CP)") cp_interaction_energy = cp_energy_body_dict[ max_nbody] - cp_energy_body_dict[1] psi4.set_variable('Counterpoise Corrected Total Energy', cp_energy_body_dict[max_nbody]) psi4.set_variable('Counterpoise Corrected Interaction Energy', cp_interaction_energy) for n in nbody_range[1:]: var_key = 'CP-CORRECTED %d-BODY INTERACTION ENERGY' % n psi4.set_variable(var_key, cp_energy_body_dict[n] - cp_energy_body_dict[1]) # Compute nocp energy and ptype if do_nocp: for n in nbody_range: if n == max_frag: nocp_energy_body_dict[n] = nocp_energy_by_level[n] if ptype != 'energy': nocp_ptype_body_dict[n][:] = nocp_ptype_by_level[n] continue for k in range(1, n + 1): take_nk = nCr(max_frag - k - 1, n - k) sign = ((-1)**(n - k)) value = nocp_energy_by_level[k] nocp_energy_body_dict[n] += take_nk * sign * value if ptype != 'energy': value = nocp_ptype_by_level[k] nocp_ptype_body_dict[n] += take_nk * sign * value _print_nbody_energy(nocp_energy_body_dict, "Non-Counterpoise Corrected (NoCP)") nocp_interaction_energy = nocp_energy_body_dict[ max_nbody] - nocp_energy_body_dict[1] psi4.set_variable('Non-Counterpoise Corrected Total Energy', nocp_energy_body_dict[max_nbody]) psi4.set_variable('Non-Counterpoise Corrected Interaction Energy', nocp_interaction_energy) for n in nbody_range[1:]: var_key = 'NOCP-CORRECTED %d-BODY INTERACTION ENERGY' % n psi4.set_variable( var_key, nocp_energy_body_dict[n] - nocp_energy_body_dict[1]) # Compute vmfc energy and ptype if do_vmfc: _print_nbody_energy(vmfc_energy_body_dict, "Valiron-Mayer Function Couterpoise (VMFC)") vmfc_interaction_energy = vmfc_energy_body_dict[ max_nbody] - vmfc_energy_body_dict[1] psi4.set_variable('Valiron-Mayer Function Couterpoise Total Energy', vmfc_energy_body_dict[max_nbody]) psi4.set_variable( 'Valiron-Mayer Function Couterpoise Interaction Energy', vmfc_interaction_energy) for n in nbody_range[1:]: var_key = 'VMFC-CORRECTED %d-BODY INTERACTION ENERGY' % n psi4.set_variable( var_key, vmfc_energy_body_dict[n] - vmfc_energy_body_dict[1]) if return_method == 'cp': ptype_body_dict = cp_ptype_body_dict energy_body_dict = cp_energy_body_dict elif return_method == 'nocp': ptype_body_dict = nocp_ptype_body_dict energy_body_dict = nocp_energy_body_dict elif return_method == 'vmfc': ptype_body_dict = vmfc_ptype_body_dict energy_body_dict = vmfc_energy_body_dict else: raise ValidationError( "N-Body Wrapper: Invalid return type. Should never be here, please post this error on github." ) # Figure out and build return types if return_total_data: ret_energy = energy_body_dict[max_nbody] else: ret_energy = energy_body_dict[max_nbody] ret_energy -= energy_body_dict[1] if ptype != 'energy': if return_total_data: np_final_ptype = ptype_body_dict[max_nbody].copy() else: np_final_ptype = ptype_body_dict[max_nbody].copy() np_final_ptype -= ptype_body_dict[1] ret_ptype = psi4.Matrix(*np_cp_final_ptype.shape) ret_ptype_view = np.asarray(final_ptype) ret_ptype_view[:] = np_final_ptype else: ret_ptype = ret_energy # Build and set a wavefunction wfn = psi4.new_wavefunction(molecule, 'sto-3g') wfn.nbody_energy = energies_dict wfn.nbody_ptype = ptype_dict wfn.nbody_body_energy = energy_body_dict wfn.nbody_body_ptype = ptype_body_dict if ptype == 'gradient': wfn.set_gradient(ret_ptype) elif ptype == 'hessian': wfn.set_hessian(ret_ptype) psi4.set_variable("CURRENT ENERGY", ret_energy) if return_wfn: return (ret_ptype, wfn) else: return ret_ptype
def frac_traverse(molecule, **kwargs): kwargs = p4util.kwargs_lower(kwargs) # The molecule is required, and should be the neutral species molecule.update_geometry() charge0 = molecule.molecular_charge() mult0 = molecule.multiplicity() chargep = charge0 + 1 chargem = charge0 - 1 # By default, the multiplicity of the cation/anion are mult0 + 1 # These are overridden with the cation_mult and anion_mult kwargs multp = kwargs.get('cation_mult', mult0 + 1) multm = kwargs.get('anion_mult', mult0 + 1) # By default, we start the frac procedure on the 25th iteration # when not reading a previous guess frac_start = kwargs.get('frac_start', 25) # By default, we occupy by tenths of electrons HOMO_occs = kwargs.get('HOMO_occs', [1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0]) LUMO_occs = kwargs.get('LUMO_occs', [1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0]) # By default, H**O and LUMO are both in alpha Z = 0; for A in range(molecule.natom()): Z += molecule.Z(A) Z -= charge0 H**O = kwargs.get('H**O', (Z / 2 + 1 if (Z % 2) else Z / 2)) LUMO = kwargs.get('LUMO', H**O + 1) # By default, DIIS in FRAC (1.0 occupation is always DIIS'd) frac_diis = kwargs.get('frac_diis', True) # By default, use the neutral orbitals as a guess for the anion neutral_guess = kwargs.get('neutral_guess', True) # By default, burn-in with UHF first, if UKS hf_guess = False if psi4.get_global_option('REFERENCE') == 'UKS': hf_guess = kwargs.get('hf_guess', True) # By default, re-guess at each N continuous_guess = kwargs.get('continuous_guess', False) # By default, drop the files to the molecule's name root = kwargs.get('filename', molecule.name()) traverse_filename = root + '.traverse.dat' # => Traverse <= # occs = [] energies = [] potentials = [] convs = [] # => Run the neutral for its orbitals, if requested <= # old_df_ints_io = psi4.get_global_option("DF_INTS_IO") psi4.set_global_option("DF_INTS_IO", "SAVE") old_guess = psi4.get_global_option("GUESS") if (neutral_guess): if (hf_guess): psi4.set_global_option("REFERENCE","UHF") energy('scf') psi4.set_global_option("GUESS", "READ") psi4.set_global_option("DF_INTS_IO", "LOAD") # => Run the anion first <= # molecule.set_molecular_charge(chargem) molecule.set_multiplicity(multm) # => Burn the anion in with hf, if requested <= # if hf_guess: psi4.set_global_option("REFERENCE","UHF") energy('scf', molecule=molecule, **kwargs) psi4.set_global_option("REFERENCE","UKS") psi4.set_global_option("GUESS", "READ") psi4.set_global_option("DF_INTS_IO", "SAVE") psi4.set_global_option("FRAC_START", frac_start) psi4.set_global_option("FRAC_RENORMALIZE", True) psi4.set_global_option("FRAC_LOAD", False) for occ in LUMO_occs: psi4.set_global_option("FRAC_OCC", [LUMO]) psi4.set_global_option("FRAC_VAL", [occ]) E, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) C = 1 if E == 0.0: E = psi4.get_variable('SCF ITERATION ENERGY') C = 0 if LUMO > 0: eps = wfn.epsilon_a() potentials.append(eps[int(LUMO) - 1]) else: eps = wfn.epsilon_b() potentials.append(eps[-int(LUMO) - 1]) occs.append(occ) energies.append(E) convs.append(C) psi4.set_global_option("FRAC_START", 2) psi4.set_global_option("FRAC_LOAD", True) psi4.set_global_option("GUESS", "READ") psi4.set_global_option("FRAC_DIIS", frac_diis) psi4.set_global_option("DF_INTS_IO", "LOAD") # => Run the neutral next <= # molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) # Burn the neutral in with hf, if requested <= # if not continuous_guess: psi4.set_global_option("GUESS", old_guess) if hf_guess: psi4.set_global_option("FRAC_START", 0) psi4.set_global_option("REFERENCE", "UHF") energy('scf', molecule=molecule, **kwargs) psi4.set_global_option("REFERENCE", "UKS") psi4.set_global_option("GUESS", "READ") psi4.set_global_option("FRAC_LOAD", False) psi4.set_global_option("FRAC_START", frac_start) psi4.set_global_option("FRAC_RENORMALIZE", True) for occ in HOMO_occs: psi4.set_global_option("FRAC_OCC", [H**O]) psi4.set_global_option("FRAC_VAL", [occ]) E, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) C = 1 if E == 0.0: E = psi4.get_variable('SCF ITERATION ENERGY') C = 0 if LUMO > 0: eps = wfn.epsilon_a() potentials.append(eps[int(H**O) - 1]) else: eps = wfn.epsilon_b() potentials.append(eps[-int(H**O) - 1]) occs.append(occ - 1.0) energies.append(E) convs.append(C) psi4.set_global_option("FRAC_START", 2) psi4.set_global_option("FRAC_LOAD", True) psi4.set_global_option("GUESS", "READ") psi4.set_global_option("FRAC_DIIS", frac_diis) psi4.set_global_option("DF_INTS_IO", "LOAD") psi4.set_global_option("DF_INTS_IO", old_df_ints_io) # => Print the results out <= # E = {} psi4.print_out("""\n ==> Fractional Occupation Traverse Results <==\n\n""") psi4.print_out("""\t%-11s %-24s %-24s %11s\n""" % ('N', 'Energy', 'H**O Energy', 'Converged')) for k in range(len(occs)): psi4.print_out("""\t%11.3E %24.16E %24.16E %11d\n""" % (occs[k], energies[k], potentials[k], convs[k])) E[occs[k]] = energies[k] psi4.print_out('\n\t"You trying to be a hero Watkins?"\n') psi4.print_out('\t"Just trying to kill some bugs sir!"\n') psi4.print_out('\t\t\t-Starship Troopers\n') # Drop the files out fh = open(traverse_filename, 'w') fh.write("""\t%-11s %-24s %-24s %11s\n""" % ('N', 'Energy', 'H**O Energy', 'Converged')) for k in range(len(occs)): fh.write("""\t%11.3E %24.16E %24.16E %11d\n""" % (occs[k], energies[k], potentials[k], convs[k])) fh.close() # Properly, should clone molecule but since not returned and easy to unblemish, molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) return E
def run_gaussian_2(name, **kwargs): # throw an exception for open-shells if (psi4.get_option('SCF','REFERENCE') != 'RHF' ): raise ValidationError("""g2 computations require "reference rhf".""") # stash user options: optstash = p4util.OptionsState( ['FNOCC','COMPUTE_TRIPLES'], ['FNOCC','COMPUTE_MP4_TRIPLES'], ['FREEZE_CORE'], ['MP2_TYPE'], ['SCF','SCF_TYPE']) # override default scf_type psi4.set_local_option('SCF','SCF_TYPE','PK') # optimize geometry at scf level psi4.clean() psi4.set_global_option('BASIS',"6-31G(D)") driver.optimize('scf') psi4.clean() # scf frequencies for zpe # NOTE This line should not be needed, but without it there's a seg fault scf_e, ref = driver.frequency('scf', return_wfn=True) # thermodynamic properties du = psi4.get_variable('INTERNAL ENERGY CORRECTION') dh = psi4.get_variable('ENTHALPY CORRECTION') dg = psi4.get_variable('GIBBS FREE ENERGY CORRECTION') freqs = ref.frequencies() nfreq = freqs.dim(0) freqsum = 0.0 for i in range(0, nfreq): freqsum += freqs.get(i) zpe = freqsum / p4const.psi_hartree2wavenumbers * 0.8929 * 0.5 psi4.clean() # optimize geometry at mp2 (no frozen core) level # note: freeze_core isn't an option in MP2 psi4.set_global_option('FREEZE_CORE',"FALSE") psi4.set_global_option('MP2_TYPE', 'CONV') driver.optimize('mp2') psi4.clean() # qcisd(t) psi4.set_local_option('FNOCC','COMPUTE_MP4_TRIPLES',"TRUE") psi4.set_global_option('FREEZE_CORE',"TRUE") psi4.set_global_option('BASIS',"6-311G(D_P)") ref = driver.proc.run_fnocc('qcisd(t)', return_wfn=True, **kwargs) # HLC: high-level correction based on number of valence electrons nirrep = ref.nirrep() frzcpi = ref.frzcpi() nfzc = 0 for i in range (0,nirrep): nfzc += frzcpi[i] nalpha = ref.nalpha() - nfzc nbeta = ref.nbeta() - nfzc # hlc of gaussian-2 hlc = -0.00481 * nalpha -0.00019 * nbeta # hlc of gaussian-1 hlc1 = -0.00614 * nalpha eqci_6311gdp = psi4.get_variable("QCISD(T) TOTAL ENERGY") emp4_6311gd = psi4.get_variable("MP4 TOTAL ENERGY") emp2_6311gd = psi4.get_variable("MP2 TOTAL ENERGY") psi4.clean() # correction for diffuse functions psi4.set_global_option('BASIS',"6-311+G(D_P)") driver.energy('mp4') emp4_6311pg_dp = psi4.get_variable("MP4 TOTAL ENERGY") emp2_6311pg_dp = psi4.get_variable("MP2 TOTAL ENERGY") psi4.clean() # correction for polarization functions psi4.set_global_option('BASIS',"6-311G(2DF_P)") driver.energy('mp4') emp4_6311g2dfp = psi4.get_variable("MP4 TOTAL ENERGY") emp2_6311g2dfp = psi4.get_variable("MP2 TOTAL ENERGY") psi4.clean() # big basis mp2 psi4.set_global_option('BASIS',"6-311+G(3DF_2P)") #run_fnocc('_mp2',**kwargs) driver.energy('mp2') emp2_big = psi4.get_variable("MP2 TOTAL ENERGY") psi4.clean() eqci = eqci_6311gdp e_delta_g2 = emp2_big + emp2_6311gd - emp2_6311g2dfp - emp2_6311pg_dp e_plus = emp4_6311pg_dp - emp4_6311gd e_2df = emp4_6311g2dfp - emp4_6311gd eg2 = eqci + e_delta_g2 + e_plus + e_2df eg2_mp2_0k = eqci + (emp2_big - emp2_6311gd) + hlc + zpe psi4.print_out('\n') psi4.print_out(' ==> G1/G2 Energy Components <==\n') psi4.print_out('\n') psi4.print_out(' QCISD(T): %20.12lf\n' % eqci) psi4.print_out(' E(Delta): %20.12lf\n' % e_delta_g2) psi4.print_out(' E(2DF): %20.12lf\n' % e_2df) psi4.print_out(' E(+): %20.12lf\n' % e_plus) psi4.print_out(' E(G1 HLC): %20.12lf\n' % hlc1) psi4.print_out(' E(G2 HLC): %20.12lf\n' % hlc) psi4.print_out(' E(ZPE): %20.12lf\n' % zpe) psi4.print_out('\n') psi4.print_out(' ==> 0 Kelvin Results <==\n') psi4.print_out('\n') eg2_0k = eg2 + zpe + hlc psi4.print_out(' G1: %20.12lf\n' % (eqci + e_plus + e_2df + hlc1 + zpe)) psi4.print_out(' G2(MP2): %20.12lf\n' % eg2_mp2_0k) psi4.print_out(' G2: %20.12lf\n' % eg2_0k) psi4.set_variable("G1 TOTAL ENERGY",eqci + e_plus + e_2df + hlc1 + zpe) psi4.set_variable("G2 TOTAL ENERGY",eg2_0k) psi4.set_variable("G2(MP2) TOTAL ENERGY",eg2_mp2_0k) psi4.print_out('\n') T = psi4.get_global_option('T') psi4.print_out(' ==> %3.0lf Kelvin Results <==\n'% T) psi4.print_out('\n') internal_energy = eg2_mp2_0k + du - zpe / 0.8929 enthalpy = eg2_mp2_0k + dh - zpe / 0.8929 gibbs = eg2_mp2_0k + dg - zpe / 0.8929 psi4.print_out(' G2(MP2) energy: %20.12lf\n' % internal_energy ) psi4.print_out(' G2(MP2) enthalpy: %20.12lf\n' % enthalpy) psi4.print_out(' G2(MP2) free energy: %20.12lf\n' % gibbs) psi4.print_out('\n') psi4.set_variable("G2(MP2) INTERNAL ENERGY",internal_energy) psi4.set_variable("G2(MP2) ENTHALPY",enthalpy) psi4.set_variable("G2(MP2) FREE ENERGY",gibbs) internal_energy = eg2_0k + du - zpe / 0.8929 enthalpy = eg2_0k + dh - zpe / 0.8929 gibbs = eg2_0k + dg - zpe / 0.8929 psi4.print_out(' G2 energy: %20.12lf\n' % internal_energy ) psi4.print_out(' G2 enthalpy: %20.12lf\n' % enthalpy) psi4.print_out(' G2 free energy: %20.12lf\n' % gibbs) psi4.set_variable("CURRENT ENERGY",eg2_0k) psi4.set_variable("G2 INTERNAL ENERGY",internal_energy) psi4.set_variable("G2 ENTHALPY",enthalpy) psi4.set_variable("G2 FREE ENERGY",gibbs) psi4.clean() optstash.restore() # return 0K g2 results return eg2_0k
def ip_fitting(molecule, omega_l, omega_r, **kwargs): kwargs = p4util.kwargs_lower(kwargs) # By default, zero the omega to 3 digits omega_tol = kwargs.get('omega_tolerance', 1.0E-3) # By default, do up to twenty iterations maxiter = kwargs.get('maxiter', 20) # By default, do not read previous 180 orbitals file read = False read180 = '' if 'read' in kwargs: read = True read180 = kwargs['read'] # The molecule is required, and should be the neutral species molecule.update_geometry() charge0 = molecule.molecular_charge() mult0 = molecule.multiplicity() # How many electrons are there? N = 0 for A in range(molecule.natom()): N += molecule.Z(A) N -= charge0 N = int(N) Nb = int((N - mult0 + 1) / 2) Na = int(N - Nb) # Work in the ot namespace for this procedure psi4.IO.set_default_namespace("ot") # Burn in to determine orbital eigenvalues if read: psi4.set_global_option("GUESS", "READ") copy_file_to_scratch(read180, 'psi', 'ot', 180) old_guess = psi4.get_global_option("GUESS") psi4.set_global_option("DF_INTS_IO", "SAVE") psi4.print_out("""\n\t==> IP Fitting SCF: Burn-in <==\n""") E, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) psi4.set_global_option("DF_INTS_IO", "LOAD") # Determine H**O, to determine mult1 eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() if Na == Nb: H**O = -Nb elif Nb == 0: H**O = Na else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if E_a >= E_b: H**O = Na else: H**O = -Nb Na1 = Na; Nb1 = Nb; if H**O > 0: Na1 = Na1 - 1; else: Nb1 = Nb1 - 1; charge1 = charge0 + 1; mult1 = Na1 - Nb1 + 1 omegas = [] E0s = [] E1s = [] kIPs = [] IPs = [] types = [] # Right endpoint psi4.set_global_option('DFT_OMEGA', omega_r) # Neutral if read: psi4.set_global_option("GUESS", "READ") p4util.copy_file_to_scratch(read180, 'psi', 'ot', 180) molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) psi4.print_out("""\n\t==> IP Fitting SCF: Neutral, Right Endpoint <==\n""") E0r, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() E_HOMO = 0.0; if Nb == 0: E_HOMO = eps_a[int(Na - 1)] else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if E_a >= E_b: E_HOMO = E_a else: E_HOMO = E_b E_HOMOr = E_HOMO psi4.IO.change_file_namespace(180, "ot", "neutral") # Cation if read: psi4.set_global_option("GUESS", "READ") p4util.copy_file_to_scratch(read180, 'psi', 'ot', 180) molecule.set_molecular_charge(charge1) molecule.set_multiplicity(mult1) psi4.print_out("""\n\t==> IP Fitting SCF: Cation, Right Endpoint <==\n""") E1r = energy('scf', molecule=molecule, **kwargs) psi4.IO.change_file_namespace(180, "ot", "cation") IPr = E1r - E0r; kIPr = -E_HOMOr; delta_r = IPr - kIPr; if IPr > kIPr: message = ("""\n***IP Fitting Error: Right Omega limit should have kIP > IP""") raise ValidationError(message) omegas.append(omega_r) types.append('Right Limit') E0s.append(E0r) E1s.append(E1r) IPs.append(IPr) kIPs.append(kIPr) # Use previous orbitals from here out psi4.set_global_option("GUESS", "READ") # Left endpoint psi4.set_global_option('DFT_OMEGA', omega_l) # Neutral psi4.IO.change_file_namespace(180, "neutral", "ot") molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) psi4.print_out("""\n\t==> IP Fitting SCF: Neutral, Left Endpoint <==\n""") E0l, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() E_HOMO = 0.0 if Nb == 0: E_HOMO = eps_a[int(Na - 1)] else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if E_a >= E_b: E_HOMO = E_a else: E_HOMO = E_b E_HOMOl = E_HOMO psi4.IO.change_file_namespace(180, "ot", "neutral") # Cation psi4.IO.change_file_namespace(180, "cation", "ot") molecule.set_molecular_charge(charge1) molecule.set_multiplicity(mult1) psi4.print_out("""\n\t==> IP Fitting SCF: Cation, Left Endpoint <==\n""") E1l = energy('scf', molecule=molecule, **kwargs) psi4.IO.change_file_namespace(180, "ot", "cation") IPl = E1l - E0l kIPl = -E_HOMOl delta_l = IPl - kIPl if IPl < kIPl: message = ("""\n***IP Fitting Error: Left Omega limit should have kIP < IP""") raise ValidationError(message) omegas.append(omega_l) types.append('Left Limit') E0s.append(E0l) E1s.append(E1l) IPs.append(IPl) kIPs.append(kIPl) converged = False repeat_l = 0 repeat_r = 0 step = 0 while True: step = step + 1 # Regula Falsi (modified) if repeat_l > 1: delta_l = delta_l / 2.0 if repeat_r > 1: delta_r = delta_r / 2.0 omega = - (omega_r - omega_l) / (delta_r - delta_l) * delta_l + omega_l psi4.set_global_option('DFT_OMEGA', omega) # Neutral psi4.IO.change_file_namespace(180, "neutral", "ot") molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) psi4.print_out("""\n\t==> IP Fitting SCF: Neutral, Omega = %11.3E <==\n""" % omega) E0, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() E_HOMO = 0.0 if Nb == 0: E_HOMO = eps_a[int(Na - 1)] else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if E_a >= E_b: E_HOMO = E_a else: E_HOMO = E_b psi4.IO.change_file_namespace(180, "ot", "neutral") # Cation psi4.IO.change_file_namespace(180, "cation", "ot") molecule.set_molecular_charge(charge1) molecule.set_multiplicity(mult1) psi4.print_out("""\n\t==> IP Fitting SCF: Cation, Omega = %11.3E <==\n""" % omega) E1 = energy('scf', molecule=molecule, **kwargs) psi4.IO.change_file_namespace(180, "ot", "cation") IP = E1 - E0 kIP = -E_HOMO delta = IP - kIP if kIP > IP: omega_r = omega E0r = E0 E1r = E1 IPr = IP kIPr = kIP delta_r = delta repeat_r = 0 repeat_l = repeat_l + 1 else: omega_l = omega E0l = E0 E1l = E1 IPl = IP kIPl = kIP delta_l = delta repeat_l = 0; repeat_r = repeat_r + 1 omegas.append(omega) types.append('Regula-Falsi') E0s.append(E0) E1s.append(E1) IPs.append(IP) kIPs.append(kIP) # Termination if (abs(omega_l - omega_r) < omega_tol or step > maxiter): converged = True break # Properly, should clone molecule but since not returned and easy to unblemish, molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) psi4.IO.set_default_namespace("") psi4.print_out("""\n\t==> IP Fitting Results <==\n\n""") psi4.print_out("""\t => Occupation Determination <= \n\n""") psi4.print_out("""\t %6s %6s %6s %6s %6s %6s\n""" % ('N', 'Na', 'Nb', 'Charge', 'Mult', 'H**O')) psi4.print_out("""\t Neutral: %6d %6d %6d %6d %6d %6d\n""" % (N, Na, Nb, charge0, mult0, H**O)) psi4.print_out("""\t Cation: %6d %6d %6d %6d %6d\n\n""" % (N - 1, Na1, Nb1, charge1, mult1)) psi4.print_out("""\t => Regula Falsi Iterations <=\n\n""") psi4.print_out("""\t%3s %11s %14s %14s %14s %s\n""" % ('N','Omega','IP','kIP','Delta','Type')) for k in range(len(omegas)): psi4.print_out("""\t%3d %11.3E %14.6E %14.6E %14.6E %s\n""" % (k + 1, omegas[k], IPs[k], kIPs[k], IPs[k] - kIPs[k], types[k])) if converged: psi4.print_out("""\n\tIP Fitting Converged\n""") psi4.print_out("""\tFinal omega = %14.6E\n""" % ((omega_l + omega_r) / 2)) psi4.print_out("""\n\t"M,I. does the dying. Fleet just does the flying."\n""") psi4.print_out("""\t\t\t-Starship Troopers\n""") else: psi4.print_out("""\n\tIP Fitting did not converge!\n""") psi4.set_global_option("DF_INTS_IO", "NONE") psi4.set_global_option("GUESS", old_guess)
def frac_nuke(molecule, **kwargs): kwargs = p4util.kwargs_lower(kwargs) # The molecule is required, and should be the neutral species molecule.update_geometry() charge0 = molecule.molecular_charge() mult0 = molecule.multiplicity() # By default, we start the frac procedure on the 25th iteration # when not reading a previous guess frac_start = kwargs.get('frac_start', 25) # By default, we occupy by tenths of electrons foccs = kwargs.get('foccs', [1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0]) # By default, H**O and LUMO are both in alpha N = 0 for A in range(molecule.natom()): N += molecule.Z(A) N -= charge0 N = int(N) Nb = int((N - mult0 + 1) / 2) Na = int(N - Nb) charge = charge0 mult = mult0 # By default, nuke all the electrons Nmin = 0 if ('nmax' in kwargs): Nmin = N - int(kwargs['nmax']) # By default, DIIS in FRAC (1.0 occupation is always DIIS'd) frac_diis = kwargs.get('frac_diis', True) # By default, drop the files to the molecule's name root = kwargs.get('filename', molecule.name()) traverse_filename = root + '.traverse.dat' stats_filename = root + '.stats.dat' # => Traverse <= # psi4.set_global_option("DF_INTS_IO", "SAVE") Ns = [] energies = [] potentials = [] convs = [] stats = [] # Run one SCF to burn things in E, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) # Determine H**O eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() if Na == Nb: H**O = -Nb elif Nb == 0: H**O = Na else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if E_a >= E_b: H**O = Na else: H**O = -Nb stats.append("""\t%6d %6d %6d %6d %6d %6d\n""" % (N, Na, Nb, charge, mult, H**O)) if H**O > 0: Na = Na - 1 else: Nb = Nb - 1 charge = charge + 1 mult = Na - Nb + 1 psi4.set_global_option("DF_INTS_IO", "LOAD") psi4.set_global_option("FRAC_START", frac_start) psi4.set_global_option("FRAC_RENORMALIZE", True) # Nuke 'em Rico! for Nintegral in range(N, Nmin, -1): # Nuke the current H**O for occ in foccs: psi4.set_global_option("FRAC_OCC", [H**O]) psi4.set_global_option("FRAC_VAL", [occ]) E, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) C = 1 if E == 0.0: E = psi4.get_variable('SCF ITERATION ENERGY') C = 0 if H**O > 0: eps = wfn.epsilon_a() potentials.append(eps[H**O - 1]) else: eps = wfn.epsilon_b() potentials.append(eps[-H**O - 1]) Ns.append(Nintegral + occ - 1.0) energies.append(E) convs.append(C) psi4.set_global_option("FRAC_START", 2) psi4.set_global_option("FRAC_LOAD", True) psi4.set_global_option("FRAC_DIIS", frac_diis) psi4.set_global_option("GUESS", "READ") # Set the next charge/mult molecule.set_molecular_charge(charge) molecule.set_multiplicity(mult) # Determine H**O print('DGAS: What ref should this point to?') #ref = psi4.legacy_wavefunction() eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() if Na == Nb: H**O = -Nb elif Nb == 0: H**O = Na else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if E_a >= E_b: H**O = Na else: H**O = -Nb stats.append("""\t%6d %6d %6d %6d %6d %6d\n""" % (Nintegral - 1, Na, Nb, charge, mult, H**O)) if H**O > 0: Na = Na - 1 else: Nb = Nb - 1 charge = charge + 1 mult = Na - Nb + 1 psi4.set_global_option("DF_INTS_IO", "NONE") # => Print the results out <= # E = {} psi4.print_out("""\n ==> Fractional Occupation Nuke Results <==\n\n""") psi4.print_out("""\t%-11s %-24s %-24s %11s\n""" % ('N', 'Energy', 'H**O Energy', 'Converged')) for k in range(len(Ns)): psi4.print_out("""\t%11.3E %24.16E %24.16E %11d\n""" % (Ns[k], energies[k], potentials[k], convs[k])) E[Ns[k]] = energies[k] psi4.print_out('\n') psi4.print_out("""\t%6s %6s %6s %6s %6s %6s\n""" % ('N', 'Na', 'Nb', 'Charge', 'Mult', 'H**O')) for line in stats: psi4.print_out(line) psi4.print_out( '\n\t"You shoot a nuke down a bug hole, you got a lot of dead bugs"\n') psi4.print_out('\t\t\t-Starship Troopers\n') # Drop the files out fh = open(traverse_filename, 'w') fh.write("""\t%-11s %-24s %-24s %11s\n""" % ('N', 'Energy', 'H**O Energy', 'Converged')) for k in range(len(Ns)): fh.write("""\t%11.3E %24.16E %24.16E %11d\n""" % (Ns[k], energies[k], potentials[k], convs[k])) fh.close() fh = open(stats_filename, 'w') fh.write("""\t%6s %6s %6s %6s %6s %6s\n""" % ('N', 'Na', 'Nb', 'Charge', 'Mult', 'H**O')) for line in stats: fh.write(line) fh.close() # Properly, should clone molecule but since not returned and easy to unblemish, molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) return E
def run_gaussian_2(name, **kwargs): # throw an exception for open-shells if (psi4.get_option('SCF', 'REFERENCE') != 'RHF'): raise ValidationError("""g2 computations require "reference rhf".""") # stash user options: optstash = p4util.OptionsState(['FNOCC', 'COMPUTE_TRIPLES'], ['FNOCC', 'COMPUTE_MP4_TRIPLES'], ['FREEZE_CORE'], ['MP2_TYPE'], ['SCF', 'SCF_TYPE']) # override default scf_type psi4.set_local_option('SCF', 'SCF_TYPE', 'PK') # optimize geometry at scf level psi4.clean() psi4.set_global_option('BASIS', "6-31G(D)") driver.optimize('scf') psi4.clean() # scf frequencies for zpe # NOTE This line should not be needed, but without it there's a seg fault scf_e, ref = driver.frequency('scf', return_wfn=True) # thermodynamic properties du = psi4.get_variable('INTERNAL ENERGY CORRECTION') dh = psi4.get_variable('ENTHALPY CORRECTION') dg = psi4.get_variable('GIBBS FREE ENERGY CORRECTION') freqs = ref.frequencies() nfreq = freqs.dim(0) freqsum = 0.0 for i in range(0, nfreq): freqsum += freqs.get(i) zpe = freqsum / p4const.psi_hartree2wavenumbers * 0.8929 * 0.5 psi4.clean() # optimize geometry at mp2 (no frozen core) level # note: freeze_core isn't an option in MP2 psi4.set_global_option('FREEZE_CORE', "FALSE") psi4.set_global_option('MP2_TYPE', 'CONV') driver.optimize('mp2') psi4.clean() # qcisd(t) psi4.set_local_option('FNOCC', 'COMPUTE_MP4_TRIPLES', "TRUE") psi4.set_global_option('FREEZE_CORE', "TRUE") psi4.set_global_option('BASIS', "6-311G(D_P)") ref = driver.proc.run_fnocc('qcisd(t)', return_wfn=True, **kwargs) # HLC: high-level correction based on number of valence electrons nirrep = ref.nirrep() frzcpi = ref.frzcpi() nfzc = 0 for i in range(0, nirrep): nfzc += frzcpi[i] nalpha = ref.nalpha() - nfzc nbeta = ref.nbeta() - nfzc # hlc of gaussian-2 hlc = -0.00481 * nalpha - 0.00019 * nbeta # hlc of gaussian-1 hlc1 = -0.00614 * nalpha eqci_6311gdp = psi4.get_variable("QCISD(T) TOTAL ENERGY") emp4_6311gd = psi4.get_variable("MP4 TOTAL ENERGY") emp2_6311gd = psi4.get_variable("MP2 TOTAL ENERGY") psi4.clean() # correction for diffuse functions psi4.set_global_option('BASIS', "6-311+G(D_P)") driver.energy('mp4') emp4_6311pg_dp = psi4.get_variable("MP4 TOTAL ENERGY") emp2_6311pg_dp = psi4.get_variable("MP2 TOTAL ENERGY") psi4.clean() # correction for polarization functions psi4.set_global_option('BASIS', "6-311G(2DF_P)") driver.energy('mp4') emp4_6311g2dfp = psi4.get_variable("MP4 TOTAL ENERGY") emp2_6311g2dfp = psi4.get_variable("MP2 TOTAL ENERGY") psi4.clean() # big basis mp2 psi4.set_global_option('BASIS', "6-311+G(3DF_2P)") #run_fnocc('_mp2',**kwargs) driver.energy('mp2') emp2_big = psi4.get_variable("MP2 TOTAL ENERGY") psi4.clean() eqci = eqci_6311gdp e_delta_g2 = emp2_big + emp2_6311gd - emp2_6311g2dfp - emp2_6311pg_dp e_plus = emp4_6311pg_dp - emp4_6311gd e_2df = emp4_6311g2dfp - emp4_6311gd eg2 = eqci + e_delta_g2 + e_plus + e_2df eg2_mp2_0k = eqci + (emp2_big - emp2_6311gd) + hlc + zpe psi4.print_out('\n') psi4.print_out(' ==> G1/G2 Energy Components <==\n') psi4.print_out('\n') psi4.print_out(' QCISD(T): %20.12lf\n' % eqci) psi4.print_out(' E(Delta): %20.12lf\n' % e_delta_g2) psi4.print_out(' E(2DF): %20.12lf\n' % e_2df) psi4.print_out(' E(+): %20.12lf\n' % e_plus) psi4.print_out(' E(G1 HLC): %20.12lf\n' % hlc1) psi4.print_out(' E(G2 HLC): %20.12lf\n' % hlc) psi4.print_out(' E(ZPE): %20.12lf\n' % zpe) psi4.print_out('\n') psi4.print_out(' ==> 0 Kelvin Results <==\n') psi4.print_out('\n') eg2_0k = eg2 + zpe + hlc psi4.print_out(' G1: %20.12lf\n' % (eqci + e_plus + e_2df + hlc1 + zpe)) psi4.print_out(' G2(MP2): %20.12lf\n' % eg2_mp2_0k) psi4.print_out(' G2: %20.12lf\n' % eg2_0k) psi4.set_variable("G1 TOTAL ENERGY", eqci + e_plus + e_2df + hlc1 + zpe) psi4.set_variable("G2 TOTAL ENERGY", eg2_0k) psi4.set_variable("G2(MP2) TOTAL ENERGY", eg2_mp2_0k) psi4.print_out('\n') T = psi4.get_global_option('T') psi4.print_out(' ==> %3.0lf Kelvin Results <==\n' % T) psi4.print_out('\n') internal_energy = eg2_mp2_0k + du - zpe / 0.8929 enthalpy = eg2_mp2_0k + dh - zpe / 0.8929 gibbs = eg2_mp2_0k + dg - zpe / 0.8929 psi4.print_out(' G2(MP2) energy: %20.12lf\n' % internal_energy) psi4.print_out(' G2(MP2) enthalpy: %20.12lf\n' % enthalpy) psi4.print_out(' G2(MP2) free energy: %20.12lf\n' % gibbs) psi4.print_out('\n') psi4.set_variable("G2(MP2) INTERNAL ENERGY", internal_energy) psi4.set_variable("G2(MP2) ENTHALPY", enthalpy) psi4.set_variable("G2(MP2) FREE ENERGY", gibbs) internal_energy = eg2_0k + du - zpe / 0.8929 enthalpy = eg2_0k + dh - zpe / 0.8929 gibbs = eg2_0k + dg - zpe / 0.8929 psi4.print_out(' G2 energy: %20.12lf\n' % internal_energy) psi4.print_out(' G2 enthalpy: %20.12lf\n' % enthalpy) psi4.print_out(' G2 free energy: %20.12lf\n' % gibbs) psi4.set_variable("CURRENT ENERGY", eg2_0k) psi4.set_variable("G2 INTERNAL ENERGY", internal_energy) psi4.set_variable("G2 ENTHALPY", enthalpy) psi4.set_variable("G2 FREE ENERGY", gibbs) psi4.clean() optstash.restore() # return 0K g2 results return eg2_0k