def __init__(self, scfwfn, mints): nocc = scfwfn.nalpha() + scfwfn.nbeta() dim = mints.basisset().nbf() * 2 Vnu = psi4.get_active_molecule().nuclear_repulsion_energy() indx = Index(dim, 'pqrsPQRS') indx.add_index_range(0, nocc, 'ijkl') indx.add_index_range(nocc, dim, 'abcd') # grab integrals and project out symmetry C1 = mints.petite_list().sotoao() # Sym -> C1 projection matrix Fa = psi4.Matrix(dim / 2, dim / 2) Fb = psi4.Matrix(dim / 2, dim / 2) Fa.remove_symmetry(scfwfn.Fa(), C1) Fb.remove_symmetry(scfwfn.Fb(), C1) Sa = mints.ao_overlap() Ta = mints.ao_kinetic() Va = mints.ao_potential() Ga = mints.ao_eri() # build and diagonalize spin-orbital Fock matrix F = block_matrix_ab(Fa, Fb) S = block_matrix_aa(Sa) s, U = la.eigh(S) U = np.matrix(U) x = 1. / np.sqrt(s).real X = U * np.diag(x) * U.T tF = X * F * X e, tC = la.eigh(tF) C = X * tC # compute integrals H = block_matrix_aa(Ta) + block_matrix_aa(Va) G = block_4darray(Ga).swapaxes(1, 2) # < mu nu | rh si >, phys. notation self.nocc, self.dim, self.Vnu, self.indx = nocc, dim, Vnu, indx self.e, self.C, self.S, self.H, self.G, self.F = e, C, S, H, G, F
def run_gpu_dfcc(name, **kwargs): """Function encoding sequence of PSI module calls for a GPU-accelerated DF-CCSD(T) computation. >>> energy('df-ccsd(t)') """ lowername = name.lower() kwargs = kwargs_lower(kwargs) # stash user options optstash = OptionsState( ['GPU_DFCC','COMPUTE_TRIPLES'], ['GPU_DFCC','DFCC'], ['GPU_DFCC','NAT_ORBS'], ['SCF','DF_INTS_IO'], ['SCF','SCF_TYPE']) psi4.set_local_option('SCF','DF_INTS_IO', 'SAVE') psi4.set_local_option('GPU_DFCC','DFCC', True) # throw an exception for open-shells if (psi4.get_option('SCF','REFERENCE') != 'RHF' ): raise ValidationError("Error: %s requires \"reference rhf\"." % lowername) # override symmetry: molecule = psi4.get_active_molecule() molecule.update_geometry() molecule.reset_point_group('c1') molecule.fix_orientation(1) molecule.update_geometry() # triples? if (lowername == 'gpu-df-ccsd'): psi4.set_local_option('GPU_DFCC','COMPUTE_TRIPLES', False) if (lowername == 'gpu-df-ccsd(t)'): psi4.set_local_option('GPU_DFCC','COMPUTE_TRIPLES', True) #if (lowername == 'fno-df-ccsd'): # psi4.set_local_option('GPU_DFCC','COMPUTE_TRIPLES', False) # psi4.set_local_option('GPU_DFCC','NAT_ORBS', True) #if (lowername == 'fno-df-ccsd(t)'): # psi4.set_local_option('GPU_DFCC','COMPUTE_TRIPLES', True) # psi4.set_local_option('GPU_DFCC','NAT_ORBS', True) # set scf-type to df unless the user wants something else if psi4.has_option_changed('SCF','SCF_TYPE') == False: psi4.set_local_option('SCF','SCF_TYPE', 'DF') if psi4.get_option('GPU_DFCC','DF_BASIS_CC') == '': basis = psi4.get_global_option('BASIS') dfbasis = corresponding_rifit(basis) psi4.set_local_option('GPU_DFCC','DF_BASIS_CC',dfbasis) scf_helper(name,**kwargs) psi4.plugin('gpu_dfcc.so') # restore options optstash.restore() return psi4.get_variable("CURRENT ENERGY")
def generate_inputs(db,name): """ Generates the input files in each sub-directory of the distributed finite differences property calculation. name: ( string ) method name passed to calling driver, db: (database) The database object associated with this property calculation. On exit this db['inputs_generated'] has been set True Returns: nothing Throws: Exception if the number of atomic displacements is not correct. """ molecule = psi4.get_active_molecule() natom = molecule.natom() # get list of displacements displacement_geoms = psi4.atomic_displacements(molecule) # Sanity Check # there should be 3 cords * natoms *2 directions (+/-) if not (6 * natom) == len(displacement_geoms): raise Exception('The number of atomic displacements should be 6 times' ' the number of atoms!') displacement_names = db['job_status'].keys() for n, entry in enumerate(displacement_names): if not os.path.exists(entry): os.makedirs(entry) # Setup up input file string inp_template = 'molecule {molname}_{disp}' inp_template += ' {{\n{molecule_info}\n}}\n{options}\n{jobspec}\n' molecule.set_geometry(displacement_geoms[n]) molecule.fix_orientation(True) molecule.fix_com(True) inputfile = open('{0}/input.dat'.format(entry), 'w') inputfile.write("# This is a psi4 input file auto-generated for" "computing properties by finite differences.\n\n") inputfile.write( inp_template.format( molname=molecule.name(), disp=entry, molecule_info=molecule.create_psi4_string_from_molecule(), options=p4util.format_options_for_input(), jobspec=db['prop_cmd'])) inputfile.close() db['inputs_generated'] = True
def initialize_database(database): database['inputs_generated'] = False database['jobs_complete'] = False database['roa_computed'] = False database['job_status'] = collections.OrderedDict() # Populate job_status molecule = psi4.get_active_molecule() natom = molecule.natom() coordinates = ['x', 'y', 'z'] step_direction = ['p', 'm'] for atom in range(1, natom + 1): for coord in coordinates: for step in step_direction: job_name = '{}_{}_{}'.format(atom, coord, step) database['job_status'].update({job_name: 'not_started'})
def __init__(self, scfwfn, mints): spinorb = SpinOrbital(scfwfn, mints) Ep2 = spinorb.build_Ep2() # Ep2 = 1/(fii+fjj-faa-fbb) K = spinorb.build_mo_K( ) # K = <p|Phi><Phi|q> single-det density matrix h = spinorb.build_mo_H() # h = <p|T+V|q> one-electron integrals g = spinorb.build_mo_antisymmetrized_G( ) # g = <pq||rs> two-electron integrals nocc, dim = spinorb.nocc, spinorb.dim indx = Index(dim, 'pqrst') indx.add_index_range(0, nocc, 'ijklm') indx.add_index_range(nocc, dim, 'abcde') # save what we need to object self.spinorb, self.indx, self.Ep2, self.K, self.h, self.g = spinorb, indx, Ep2, K, h, g self.E, self.Vnu = 0.0, psi4.get_active_molecule( ).nuclear_repulsion_energy()
def initialize_database(database): database['inputs_generated'] = False database['jobs_complete'] = False database['roa_computed'] = False database['job_status'] = collections.OrderedDict() # Populate job_status molecule = psi4.get_active_molecule() natom = molecule.natom() coordinates = ['x','y','z'] step_direction = ['p','m'] for atom in range(1, natom+1): for coord in coordinates: for step in step_direction: job_name = '{}_{}_{}'.format(atom,coord,step) database['job_status'].update({job_name: 'not_started'})
def initialize_database(database, name, prop, properties_array, additional_kwargs=None): """ Initialize the database for computation of some property using distributed finite differences driver database: (database) the database object passed from the caller name: (string) name as passed to calling driver prop: (string) the property being computed, used to add xxx_computed flag to database prop_array: (list of strings) properties to go in properties kwarg of the property() cmd in each sub-dir additional_kwargs: (list of strings) *optional* any additional kwargs that should go in the call to the property() driver method in each subdir Returns: nothing Throws: nothing """ database['inputs_generated'] = False database['jobs_complete'] = False prop_cmd ="property('{0}',".format(name) prop_cmd += "properties=[ '{}' ".format(properties_array[0]) if len(properties_array) > 1: for element in properties_array[1:]: prop_cmd += ",'{}'".format(element) prop_cmd += "]" if additional_kwargs is not None: for arg in additional_kwargs: prop_cmd += ", {}".format(arg) prop_cmd += ")" database['prop_cmd'] = prop_cmd database['job_status'] = collections.OrderedDict() # Populate the job_status dict molecule = psi4.get_active_molecule() natom = molecule.natom() coordinates = ['x', 'y', 'z'] step_direction = ['p', 'm'] for atom in range(1, natom + 1): for coord in coordinates: for step in step_direction: job_name = '{}_{}_{}'.format(atom, coord, step) database['job_status'].update({job_name: 'not_started'}) database['{}_computed'.format(prop)] = False
def generate_inputs(name, db): molecule = psi4.get_active_molecule() natom = molecule.natom() # Get list of displacements displacement_geoms = psi4.atomic_displacements() # Sanity check! # Until we append the original geometry if not (6 * natom) == len(displacement_geoms): raise Exception('The number displacements should be 6 times the number' 'of atoms!') # List of displacements for iterating displacement_names = db['job_status'].keys() for n, entry in enumerate(displacement_names): if not os.path.exists(entry): os.makedirs(entry) # Set up mol_open = 'molecule ' + molecule.name() + '_' + entry + ' {\n' mol_close = '}' molecule.set_geometry(displacement_geoms[n]) molecule.fix_orientation(True) molecule.fix_com(True) # Write input file inputfile = open('{0}/input.dat'.format(entry), 'w') inputfile.write("# This is a psi4 input file auto-generated for " "computing Raman Optical Activity.\n\n") #inputfile.write(basic_molecule_for_input(molecule)) inputfile.write("{}{}{}".format( mol_open, molecule.create_psi4_string_from_molecule(), mol_close)) #.format(mol_open,molecule.save_string_xyz(),mol_close)) inputfile.write('\n') inputfile.write(p4util.format_options_for_input()) inputfile.write('\n') inputfile.write( "property('{0}', properties=['roa_tensor'])".format(name)) inputfile.close()
def generate_inputs(name,db): molecule = psi4.get_active_molecule() natom = molecule.natom() # Get list of displacements displacement_geoms = psi4.atomic_displacements(molecule) # Sanity check! # Until we append the original geometry if not (6*natom) == len(displacement_geoms): raise Exception('The number displacements should be 6 times the number' 'of atoms!') # List of displacements for iterating displacement_names = db['job_status'].keys() for n,entry in enumerate(displacement_names): if not os.path.exists(entry): os.makedirs(entry) # Set up mol_open = 'molecule ' + molecule.name() + '_' + entry + ' {\n' mol_close = '}' molecule.set_geometry(displacement_geoms[n]) molecule.fix_orientation(True) molecule.fix_com(True) # Write input file inputfile = open('{0}/input.dat'.format(entry), 'w') inputfile.write("# This is a psi4 input file auto-generated for " "computing Raman Optical Activity.\n\n") #inputfile.write(basic_molecule_for_input(molecule)) inputfile.write("{}{}{}" .format(mol_open,molecule.create_psi4_string_from_molecule(),mol_close)) #.format(mol_open,molecule.save_string_xyz(),mol_close)) inputfile.write('\n') inputfile.write(p4util.format_options_for_input()) inputfile.write('\n') inputfile.write("property('{0}', properties=['roa_tensor'])".format(name)) inputfile.close()
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 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.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 # 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(): 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 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): # 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 _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 _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 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 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 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): # 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 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()