def run_gaussian_2(name, **kwargs): # throw an exception for open-shells if (core.get_option('SCF', 'REFERENCE') != 'RHF'): raise ValidationError("""g2 computations require "reference rhf".""") # stash user options: optstash = p4util.OptionsState(['FNOCC', 'COMPUTE_TRIPLES'], ['FNOCC', 'COMPUTE_MP4_TRIPLES'], ['BASIS'], ['FREEZE_CORE'], ['MP2_TYPE'], ['SCF_TYPE']) # override default scf_type core.set_global_option('SCF_TYPE', 'PK') # optimize geometry at scf level core.clean() core.set_global_option('BASIS', "6-31G(D)") driver.optimize('scf') core.clean() # scf frequencies for zpe # NOTE This line should not be needed, but without it there's a seg fault scf_e, ref = driver.frequency('scf', return_wfn=True) # thermodynamic properties du = core.variable('THERMAL ENERGY CORRECTION') dh = core.variable('ENTHALPY CORRECTION') dg = core.variable('GIBBS FREE ENERGY CORRECTION') freqs = ref.frequencies() nfreq = freqs.dim(0) freqsum = 0.0 for i in range(0, nfreq): freqsum += freqs.get(i) zpe = freqsum / constants.hartree2wavenumbers * 0.8929 * 0.5 core.clean() # optimize geometry at mp2 (no frozen core) level # note: freeze_core isn't an option in MP2 core.set_global_option('FREEZE_CORE', "FALSE") core.set_global_option('MP2_TYPE', 'CONV') driver.optimize('mp2') core.clean() # qcisd(t) core.set_local_option('FNOCC', 'COMPUTE_MP4_TRIPLES', "TRUE") core.set_global_option('FREEZE_CORE', "TRUE") core.set_global_option('BASIS', "6-311G(D_P)") ref = driver.proc.run_fnocc('qcisd(t)', return_wfn=True, **kwargs) # HLC: high-level correction based on number of valence electrons nirrep = ref.nirrep() frzcpi = ref.frzcpi() nfzc = 0 for i in range(0, nirrep): nfzc += frzcpi[i] nalpha = ref.nalpha() - nfzc nbeta = ref.nbeta() - nfzc # hlc of gaussian-2 hlc = -0.00481 * nalpha - 0.00019 * nbeta # hlc of gaussian-1 hlc1 = -0.00614 * nalpha eqci_6311gdp = core.variable("QCISD(T) TOTAL ENERGY") emp4_6311gd = core.variable("MP4 TOTAL ENERGY") emp2_6311gd = core.variable("MP2 TOTAL ENERGY") core.clean() # correction for diffuse functions core.set_global_option('BASIS', "6-311+G(D_P)") driver.energy('mp4') emp4_6311pg_dp = core.variable("MP4 TOTAL ENERGY") emp2_6311pg_dp = core.variable("MP2 TOTAL ENERGY") core.clean() # correction for polarization functions core.set_global_option('BASIS', "6-311G(2DF_P)") driver.energy('mp4') emp4_6311g2dfp = core.variable("MP4 TOTAL ENERGY") emp2_6311g2dfp = core.variable("MP2 TOTAL ENERGY") core.clean() # big basis mp2 core.set_global_option('BASIS', "6-311+G(3DF_2P)") #run_fnocc('_mp2',**kwargs) driver.energy('mp2') emp2_big = core.variable("MP2 TOTAL ENERGY") core.clean() eqci = eqci_6311gdp e_delta_g2 = emp2_big + emp2_6311gd - emp2_6311g2dfp - emp2_6311pg_dp e_plus = emp4_6311pg_dp - emp4_6311gd e_2df = emp4_6311g2dfp - emp4_6311gd eg2 = eqci + e_delta_g2 + e_plus + e_2df eg2_mp2_0k = eqci + (emp2_big - emp2_6311gd) + hlc + zpe core.print_out('\n') core.print_out(' ==> G1/G2 Energy Components <==\n') core.print_out('\n') core.print_out(' QCISD(T): %20.12lf\n' % eqci) core.print_out(' E(Delta): %20.12lf\n' % e_delta_g2) core.print_out(' E(2DF): %20.12lf\n' % e_2df) core.print_out(' E(+): %20.12lf\n' % e_plus) core.print_out(' E(G1 HLC): %20.12lf\n' % hlc1) core.print_out(' E(G2 HLC): %20.12lf\n' % hlc) core.print_out(' E(ZPE): %20.12lf\n' % zpe) core.print_out('\n') core.print_out(' ==> 0 Kelvin Results <==\n') core.print_out('\n') eg2_0k = eg2 + zpe + hlc core.print_out(' G1: %20.12lf\n' % (eqci + e_plus + e_2df + hlc1 + zpe)) core.print_out(' G2(MP2): %20.12lf\n' % eg2_mp2_0k) core.print_out(' G2: %20.12lf\n' % eg2_0k) core.set_variable("G1 TOTAL ENERGY", eqci + e_plus + e_2df + hlc1 + zpe) core.set_variable("G2 TOTAL ENERGY", eg2_0k) core.set_variable("G2(MP2) TOTAL ENERGY", eg2_mp2_0k) core.print_out('\n') T = core.get_global_option('T') core.print_out(' ==> %3.0lf Kelvin Results <==\n' % T) core.print_out('\n') internal_energy = eg2_mp2_0k + du - zpe / 0.8929 enthalpy = eg2_mp2_0k + dh - zpe / 0.8929 gibbs = eg2_mp2_0k + dg - zpe / 0.8929 core.print_out(' G2(MP2) energy: %20.12lf\n' % internal_energy) core.print_out(' G2(MP2) enthalpy: %20.12lf\n' % enthalpy) core.print_out(' G2(MP2) free energy: %20.12lf\n' % gibbs) core.print_out('\n') core.set_variable("G2(MP2) INTERNAL ENERGY", internal_energy) core.set_variable("G2(MP2) ENTHALPY", enthalpy) core.set_variable("G2(MP2) FREE ENERGY", gibbs) internal_energy = eg2_0k + du - zpe / 0.8929 enthalpy = eg2_0k + dh - zpe / 0.8929 gibbs = eg2_0k + dg - zpe / 0.8929 core.print_out(' G2 energy: %20.12lf\n' % internal_energy) core.print_out(' G2 enthalpy: %20.12lf\n' % enthalpy) core.print_out(' G2 free energy: %20.12lf\n' % gibbs) core.set_variable("CURRENT ENERGY", eg2_0k) core.set_variable("G2 INTERNAL ENERGY", internal_energy) core.set_variable("G2 ENTHALPY", enthalpy) core.set_variable("G2 FREE ENERGY", gibbs) core.clean() optstash.restore() # return 0K g2 results return eg2_0k
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 = p4util.kwargs_lower(kwargs) # stash user options optstash = p4util.OptionsState(['GPU_DFCC', 'COMPUTE_TRIPLES'], ['GPU_DFCC', 'DFCC'], ['GPU_DFCC', 'NAT_ORBS'], ['SCF', 'DF_INTS_IO'], ['SCF', 'SCF_TYPE'], ['GPU_DFCC', 'CC_TYPE']) psi4.core.set_local_option('GPU_DFCC', 'DFCC', True) psi4.core.set_local_option('GPU_DFCC', 'CC_TYPE', 'DF') # throw an exception for open-shells if (psi4.core.get_option('SCF', 'REFERENCE') != 'RHF'): raise ValidationError("Error: %s requires \"reference rhf\"." % lowername) def set_cholesky_from(mtd_type): type_val = core.get_global_option(mtd_type) if type_val == 'CD': core.set_local_option('GPU_DFCC', 'DF_BASIS_CC', 'CHOLESKY') # Alter default algorithm if not core.has_global_option_changed('SCF_TYPE'): optstash.add_option(['SCF_TYPE']) core.set_global_option('SCF_TYPE', 'CD') core.print_out(""" SCF Algorithm Type (re)set to CD.\n""") elif type_val == 'DF': if core.get_option('GPU_DFCC', 'DF_BASIS_CC') == 'CHOLESKY': core.set_local_option('GPU_DFCC', 'DF_BASIS_CC', '') proc_util.check_disk_df(name.upper(), optstash) else: raise ValidationError("""Invalid type '%s' for DFCC""" % type_val) # triples? if (lowername == 'gpu-df-ccsd'): psi4.core.set_local_option('GPU_DFCC', 'COMPUTE_TRIPLES', False) set_cholesky_from('CC_TYPE') if (lowername == 'gpu-df-ccsd(t)'): psi4.core.set_local_option('GPU_DFCC', 'COMPUTE_TRIPLES', True) set_cholesky_from('CC_TYPE') if psi4.core.get_global_option('SCF_TYPE') not in ['CD', 'DISK_DF']: raise ValidationError("""Invalid scf_type for DFCC.""") # save DF or CD ints generated by SCF for use in CC psi4.core.set_local_option('SCF', 'DF_INTS_IO', 'SAVE') # psi4 run sequence ref_wfn = kwargs.get('ref_wfn', None) if ref_wfn is None: ref_wfn = psi4.driver.scf_helper(name, use_c1=True, **kwargs) # C1 certified else: if ref_wfn.molecule().schoenflies_symbol() != 'c1': raise ValidationError( """ GPU-DFCC does not make use of molecular symmetry: """ """ reference wavefunction must be C1.\n""") scf_aux_basis = psi4.core.BasisSet.build( ref_wfn.molecule(), "DF_BASIS_SCF", psi4.core.get_option("SCF", "DF_BASIS_SCF"), "JKFIT", psi4.core.get_global_option('BASIS'), puream=ref_wfn.basisset().has_puream()) ref_wfn.set_basisset("DF_BASIS_SCF", scf_aux_basis) aux_basis = psi4.core.BasisSet.build( ref_wfn.molecule(), "DF_BASIS_CC", psi4.core.get_global_option("DF_BASIS_CC"), "RIFIT", psi4.core.get_global_option("BASIS")) ref_wfn.set_basisset("DF_BASIS_CC", aux_basis) returnvalue = psi4.core.plugin('gpu_dfcc.so', ref_wfn) # restore options optstash.restore() return returnvalue
def _set_convergence_criterion(ptype, method_name, scf_Ec, pscf_Ec, scf_Dc, pscf_Dc, gen_Ec, verbose=1): r""" This function will set local SCF and global energy convergence criterion to the defaults listed at: http://www.psicode.org/psi4manual/master/scf.html#convergence-and- algorithm-defaults. SCF will be converged more tightly if a post-SCF method is select (pscf_Ec, and pscf_Dc) else the looser (scf_Ec, and scf_Dc convergence criterion will be used). ptype - Procedure type (energy, gradient, etc). Nearly always test on procedures['energy'] since that's guaranteed to exist for a method. method_name - Name of the method scf_Ec - E convergence criterion for scf target method pscf_Ec - E convergence criterion for scf of post-scf target method scf_Dc - D convergence criterion for scf target method pscf_Dc - D convergence criterion for scf of post-scf target method gen_Ec - E convergence criterion for post-scf target method """ optstash = p4util.OptionsState(['SCF', 'E_CONVERGENCE'], ['SCF', 'D_CONVERGENCE'], ['E_CONVERGENCE']) # Kind of want to move this out of here _method_exists(ptype, method_name) if verbose >= 2: print(' Setting convergence', end=' ') # Set method-dependent scf convergence criteria, check against energy routines if not core.has_option_changed('SCF', 'E_CONVERGENCE'): if procedures['energy'][method_name] in [ proc.run_scf, proc.run_tdscf_energy ]: core.set_local_option('SCF', 'E_CONVERGENCE', scf_Ec) if verbose >= 2: print(scf_Ec, end=' ') else: core.set_local_option('SCF', 'E_CONVERGENCE', pscf_Ec) if verbose >= 2: print(pscf_Ec, end=' ') else: if verbose >= 2: print('CUSTOM', core.get_option('SCF', 'E_CONVERGENCE'), end=' ') if not core.has_option_changed('SCF', 'D_CONVERGENCE'): if procedures['energy'][method_name] in [ proc.run_scf, proc.run_tdscf_energy ]: core.set_local_option('SCF', 'D_CONVERGENCE', scf_Dc) if verbose >= 2: print(scf_Dc, end=' ') else: core.set_local_option('SCF', 'D_CONVERGENCE', pscf_Dc) if verbose >= 2: print(pscf_Dc, end=' ') else: if verbose >= 2: print('CUSTOM', core.get_option('SCF', 'D_CONVERGENCE'), end=' ') # Set post-scf convergence criteria (global will cover all correlated modules) if not core.has_global_option_changed('E_CONVERGENCE'): if procedures['energy'][method_name] != proc.run_scf: core.set_global_option('E_CONVERGENCE', gen_Ec) if verbose >= 2: print(gen_Ec, end=' ') else: if procedures['energy'][method_name] != proc.run_scf: if verbose >= 2: print('CUSTOM', core.get_global_option('E_CONVERGENCE'), end=' ') if verbose >= 2: print('') return optstash
def run_sapt_dft(name, **kwargs): optstash = p4util.OptionsState(['SCF', 'SCF_TYPE'], ['SCF', 'REFERENCE'], ['SCF', 'DFT_GRAC_SHIFT'], ['SCF', 'SAVE_JK']) core.tstart() # Alter default algorithm if not core.has_option_changed('SCF', 'SCF_TYPE'): core.set_local_option('SCF', 'SCF_TYPE', 'DF') core.prepare_options_for_module("SAPT") # Get the molecule of interest ref_wfn = kwargs.get('ref_wfn', None) if ref_wfn is None: sapt_dimer = kwargs.pop('molecule', core.get_active_molecule()) else: core.print_out( 'Warning! SAPT argument "ref_wfn" is only able to use molecule information.' ) sapt_dimer = ref_wfn.molecule() sapt_dimer, monomerA, monomerB = proc_util.prepare_sapt_molecule( sapt_dimer, "dimer") # Grab overall settings mon_a_shift = core.get_option("SAPT", "SAPT_DFT_GRAC_SHIFT_A") mon_b_shift = core.get_option("SAPT", "SAPT_DFT_GRAC_SHIFT_B") do_delta_hf = core.get_option("SAPT", "SAPT_DFT_DO_DHF") sapt_dft_functional = core.get_option("SAPT", "SAPT_DFT_FUNCTIONAL") # Print out the title and some information core.print_out("\n") core.print_out( " ---------------------------------------------------------\n") core.print_out(" " + "SAPT(DFT) Procedure".center(58) + "\n") core.print_out("\n") core.print_out(" " + "by Daniel G. A. Smith".center(58) + "\n") core.print_out( " ---------------------------------------------------------\n") core.print_out("\n") core.print_out(" ==> Algorithm <==\n\n") core.print_out(" SAPT DFT Functional %12s\n" % str(sapt_dft_functional)) core.print_out(" Monomer A GRAC Shift %12.6f\n" % mon_a_shift) core.print_out(" Monomer B GRAC Shift %12.6f\n" % mon_b_shift) core.print_out(" Delta HF %12s\n" % ("True" if do_delta_hf else "False")) core.print_out(" JK Algorithm %12s\n" % core.get_option("SCF", "SCF_TYPE")) core.print_out("\n") core.print_out(" Required computations:\n") if (do_delta_hf): core.print_out(" HF (Dimer)\n") core.print_out(" HF (Monomer A)\n") core.print_out(" HF (Monomer B)\n") core.print_out(" DFT (Monomer A)\n") core.print_out(" DFT (Monomer B)\n") core.print_out("\n") if (sapt_dft_functional != "HF") and ((mon_a_shift == 0.0) or (mon_b_shift == 0.0)): raise ValidationError( 'SAPT(DFT): must set both "SAPT_DFT_GRAC_SHIFT_A" and "B".') if (core.get_option('SCF', 'REFERENCE') != 'RHF'): raise ValidationError( 'SAPT(DFT) currently only supports restricted references.') core.IO.set_default_namespace('dimer') data = {} if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): # core.set_global_option('DF_INTS_IO', 'LOAD') core.set_global_option('DF_INTS_IO', 'SAVE') # # Compute dimer wavefunction hf_cache = {} hf_wfn_dimer = None if do_delta_hf: if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.set_global_option('DF_INTS_IO', 'SAVE') hf_data = {} hf_wfn_dimer = scf_helper("SCF", molecule=sapt_dimer, banner="SAPT(DFT): delta HF Dimer", **kwargs) hf_data["HF DIMER"] = core.get_variable("CURRENT ENERGY") if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.IO.change_file_namespace(97, 'dimer', 'monomerA') hf_wfn_A = scf_helper("SCF", molecule=monomerA, banner="SAPT(DFT): delta HF Monomer A", **kwargs) hf_data["HF MONOMER A"] = core.get_variable("CURRENT ENERGY") core.set_global_option("SAVE_JK", True) if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.IO.change_file_namespace(97, 'monomerA', 'monomerB') hf_wfn_B = scf_helper("SCF", molecule=monomerB, banner="SAPT(DFT): delta HF Monomer B", **kwargs) hf_data["HF MONOMER B"] = core.get_variable("CURRENT ENERGY") core.set_global_option("SAVE_JK", False) # Move it back to monomer A if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.IO.change_file_namespace(97, 'monomerB', 'dimer') core.print_out("\n") core.print_out( " ---------------------------------------------------------\n" ) core.print_out(" " + "SAPT(DFT): delta HF Segement".center(58) + "\n") core.print_out("\n") core.print_out(" " + "by Daniel G. A. Smith and Rob Parrish".center(58) + "\n") core.print_out( " ---------------------------------------------------------\n" ) core.print_out("\n") # Build cache and JK sapt_jk = hf_wfn_B.jk() hf_cache = sapt_jk_terms.build_sapt_jk_cache(hf_wfn_A, hf_wfn_B, sapt_jk, True) # Electostatics elst = sapt_jk_terms.electrostatics(hf_cache, True) hf_data.update(elst) # Exchange exch = sapt_jk_terms.exchange(hf_cache, sapt_jk, True) hf_data.update(exch) # Induction ind = sapt_jk_terms.induction( hf_cache, sapt_jk, True, maxiter=core.get_option("SAPT", "MAXITER"), conv=core.get_option("SAPT", "D_CONVERGENCE"), Sinf=core.get_option("SAPT", "DO_IND_EXCH_SINF")) hf_data.update(ind) dhf_value = hf_data["HF DIMER"] - hf_data["HF MONOMER A"] - hf_data[ "HF MONOMER B"] core.print_out("\n") core.print_out( print_sapt_hf_summary(hf_data, "SAPT(HF)", delta_hf=dhf_value)) data["Delta HF Correction"] = core.get_variable("SAPT(DFT) Delta HF") sapt_jk.finalize() if hf_wfn_dimer is None: dimer_wfn = core.Wavefunction.build(sapt_dimer, core.get_global_option("BASIS")) else: dimer_wfn = hf_wfn_dimer # Set the primary functional core.set_local_option('SCF', 'REFERENCE', 'RKS') # Compute Monomer A wavefunction if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.IO.change_file_namespace(97, 'dimer', 'monomerA') if mon_a_shift: core.set_global_option("DFT_GRAC_SHIFT", mon_a_shift) # Save the JK object core.IO.set_default_namespace('monomerA') wfn_A = scf_helper(sapt_dft_functional, post_scf=False, molecule=monomerA, banner="SAPT(DFT): DFT Monomer A", **kwargs) data["DFT MONOMERA"] = core.get_variable("CURRENT ENERGY") core.set_global_option("DFT_GRAC_SHIFT", 0.0) # Compute Monomer B wavefunction if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.IO.change_file_namespace(97, 'monomerA', 'monomerB') if mon_b_shift: core.set_global_option("DFT_GRAC_SHIFT", mon_b_shift) core.set_global_option("SAVE_JK", True) core.IO.set_default_namespace('monomerB') wfn_B = scf_helper(sapt_dft_functional, post_scf=False, molecule=monomerB, banner="SAPT(DFT): DFT Monomer B", **kwargs) data["DFT MONOMERB"] = core.get_variable("CURRENT ENERGY") core.set_global_option("DFT_GRAC_SHIFT", 0.0) # Write out header scf_alg = core.get_option("SCF", "SCF_TYPE") sapt_dft_header(sapt_dft_functional, mon_a_shift, mon_b_shift, bool(do_delta_hf), scf_alg) # Call SAPT(DFT) sapt_jk = wfn_B.jk() sapt_dft(dimer_wfn, wfn_A, wfn_B, sapt_jk=sapt_jk, data=data, print_header=False) # Copy data back into globals for k, v in data.items(): core.set_variable(k, v) core.tstop() return dimer_wfn
def ip_fitting(name, omega_l=0.05, omega_r=2.5, omega_convergence=1.0e-3, maxiter=20, **kwargs): """Optimize DFT omega parameter for molecular system. Parameters ---------- name : string or function DFT functional string name or function defining functional whose omega is to be optimized. omega_l : float, optional Minimum omega to be considered during fitting. omega_r : float, optional Maximum omega to be considered during fitting. molecule : :ref:`molecule <op_py_molecule>`, optional Target molecule (neutral) for which omega is to be tuned, if not last defined. omega_convergence : float, optional Threshold below which to consider omega converged. (formerly omega_tolerance) maxiter : int, optional Maximum number of iterations towards omega convergence. Returns ------- float Optimal omega parameter. """ optstash = p4util.OptionsState(['SCF', 'REFERENCE'], ['SCF', 'GUESS'], ['SCF', 'DF_INTS_IO'], ['SCF', 'DFT_OMEGA'], ['DOCC'], ['SOCC']) kwargs = p4util.kwargs_lower(kwargs) # By default, do not read previous 180 orbitals file read = False read180 = '' if 'read' in kwargs: read = True read180 = kwargs['read'] if core.get_option('SCF', 'REFERENCE') != 'UKS': core.print_out( """ Requested procedure `ip_fitting` runs further calculations with UKS reference.\n""" ) core.set_local_option('SCF', 'REFERENCE', 'UKS') # Make sure the molecule the user provided is the active one, and neutral molecule = kwargs.pop('molecule', core.get_active_molecule()) molecule.update_geometry() if molecule.molecular_charge() != 0: raise ValidationError( """IP Fitting requires neutral molecule to start.""") if molecule.schoenflies_symbol() != 'c1': core.print_out( """ Requested procedure `ip_fitting` does not make use of molecular symmetry: """ """further calculations in C1 point group.\n""") molecule = molecule.clone() molecule.reset_point_group('c1') molecule.update_geometry() charge0 = molecule.molecular_charge() mult0 = molecule.multiplicity() # How many electrons are there? N = 0 for A in range(molecule.natom()): N += molecule.Z(A) N -= charge0 N = int(N) Nb = int((N - mult0 + 1) / 2) Na = int(N - Nb) # Work in the ot namespace for this procedure core.IO.set_default_namespace("ot") # Burn in to determine orbital eigenvalues if read: core.set_local_option("SCF", "GUESS", "READ") copy_file_to_scratch(read180, 'psi', 'ot', 180) core.set_local_option("SCF", "DF_INTS_IO", "SAVE") E, wfn = driver.energy('scf', dft_functional=name, return_wfn=True, molecule=molecule, banner='IP Fitting SCF: Burn-in', **kwargs) core.set_local_option("SCF", "DF_INTS_IO", "LOAD") if not wfn.functional().is_x_lrc(): raise ValidationError( """Not sensible to optimize omega for non-long-range-correction functional.""" ) # Determine H**O, to determine mult1 eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() if Na == Nb: H**O = -Nb elif Nb == 0: H**O = Na else: E_a = eps_a.np[int(Na - 1)] E_b = eps_b.np[int(Nb - 1)] if E_a >= E_b: H**O = Na else: H**O = -Nb Na1 = Na Nb1 = Nb if H**O > 0: Na1 -= 1 else: Nb1 -= 1 charge1 = charge0 + 1 mult1 = Na1 - Nb1 + 1 omegas = [] E0s = [] E1s = [] kIPs = [] IPs = [] types = [] # Right endpoint core.set_local_option('SCF', 'DFT_OMEGA', omega_r) # Neutral if read: core.set_local_option("SCF", "GUESS", "READ") p4util.copy_file_to_scratch(read180, 'psi', 'ot', 180) molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) E0r, wfn = driver.energy('scf', dft_functional=name, return_wfn=True, molecule=molecule, banner='IP Fitting SCF: Neutral, Right Endpoint', **kwargs) eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() if Nb == 0: E_HOMO = eps_a.np[int(Na - 1)] else: E_a = eps_a.np[int(Na - 1)] E_b = eps_b.np[int(Nb - 1)] E_HOMO = max(E_a, E_b) E_HOMOr = E_HOMO core.IO.change_file_namespace(180, "ot", "neutral") # Cation if read: core.set_local_option("SCF", "GUESS", "READ") p4util.copy_file_to_scratch(read180, 'psi', 'ot', 180) molecule.set_molecular_charge(charge1) molecule.set_multiplicity(mult1) E1r = driver.energy('scf', dft_functional=name, molecule=molecule, banner='IP Fitting SCF: Cation, Right Endpoint', **kwargs) core.IO.change_file_namespace(180, "ot", "cation") IPr = E1r - E0r kIPr = -E_HOMOr delta_r = IPr - kIPr if IPr > kIPr: raise ValidationError( """\n***IP Fitting Error: Right Omega limit should have kIP > IP: {} !> {}""" .format(kIPr, IPr)) omegas.append(omega_r) types.append('Right Limit') E0s.append(E0r) E1s.append(E1r) IPs.append(IPr) kIPs.append(kIPr) # Use previous orbitals from here out core.set_local_option("SCF", "GUESS", "READ") # Left endpoint core.set_local_option('SCF', 'DFT_OMEGA', omega_l) # Neutral core.IO.change_file_namespace(180, "neutral", "ot") molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) core.set_global_option("DOCC", [Nb]) core.set_global_option("SOCC", [Na - Nb]) E0l, wfn = driver.energy('scf', dft_functional=name, return_wfn=True, molecule=molecule, banner='IP Fitting SCF: Neutral, Left Endpoint', **kwargs) eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() if Nb == 0: E_HOMO = eps_a.np[int(Na - 1)] else: E_a = eps_a.np[int(Na - 1)] E_b = eps_b.np[int(Nb - 1)] E_HOMO = max(E_a, E_b) E_HOMOl = E_HOMO core.IO.change_file_namespace(180, "ot", "neutral") # Cation core.IO.change_file_namespace(180, "cation", "ot") molecule.set_molecular_charge(charge1) molecule.set_multiplicity(mult1) core.set_global_option("DOCC", [Nb1]) core.set_global_option("SOCC", [Na1 - Nb1]) E1l = driver.energy('scf', dft_functional=name, molecule=molecule, banner='IP Fitting SCF: Cation, Left Endpoint', **kwargs) core.IO.change_file_namespace(180, "ot", "cation") IPl = E1l - E0l kIPl = -E_HOMOl delta_l = IPl - kIPl if IPl < kIPl: raise ValidationError( """\n***IP Fitting Error: Left Omega limit should have kIP < IP: {} !< {}""" .format(kIPl, IPl)) omegas.append(omega_l) types.append('Left Limit') E0s.append(E0l) E1s.append(E1l) IPs.append(IPl) kIPs.append(kIPl) converged = False repeat_l = 0 repeat_r = 0 for step in range(maxiter): # Regula Falsi (modified) if repeat_l > 1: delta_l /= 2.0 if repeat_r > 1: delta_r /= 2.0 omega = -(omega_r - omega_l) / (delta_r - delta_l) * delta_l + omega_l core.set_local_option('SCF', 'DFT_OMEGA', omega) # Neutral core.IO.change_file_namespace(180, "neutral", "ot") molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) core.set_global_option("DOCC", [Nb]) core.set_global_option("SOCC", [Na - Nb]) E0, wfn = driver.energy( 'scf', dft_functional=name, return_wfn=True, molecule=molecule, banner='IP Fitting SCF: Neutral, Omega = {:11.3E}'.format(omega), **kwargs) eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() if Nb == 0: E_HOMO = eps_a.np[int(Na - 1)] else: E_a = eps_a.np[int(Na - 1)] E_b = eps_b.np[int(Nb - 1)] E_HOMO = max(E_a, E_b) core.IO.change_file_namespace(180, "ot", "neutral") # Cation core.IO.change_file_namespace(180, "cation", "ot") molecule.set_molecular_charge(charge1) molecule.set_multiplicity(mult1) core.set_global_option("DOCC", [Nb1]) core.set_global_option("SOCC", [Na1 - Nb1]) E1 = driver.energy( 'scf', dft_functional=name, molecule=molecule, banner='IP Fitting SCF: Cation, Omega = {:11.3E}'.format(omega), **kwargs) core.IO.change_file_namespace(180, "ot", "cation") IP = E1 - E0 kIP = -E_HOMO delta = IP - kIP if kIP > IP: omega_r = omega E0r = E0 E1r = E1 IPr = IP kIPr = kIP delta_r = delta repeat_r = 0 repeat_l += 1 else: omega_l = omega E0l = E0 E1l = E1 IPl = IP kIPl = kIP delta_l = delta repeat_l = 0 repeat_r += 1 omegas.append(omega) types.append('Regula-Falsi') E0s.append(E0) E1s.append(E1) IPs.append(IP) kIPs.append(kIP) # Termination if abs(omega_l - omega_r) < omega_convergence: converged = True break core.IO.set_default_namespace("") core.print_out("""\n ==> IP Fitting Results <==\n\n""") core.print_out(""" => Occupation Determination <= \n\n""") core.print_out(""" %6s %6s %6s %6s %6s %6s\n""" % ('N', 'Na', 'Nb', 'Charge', 'Mult', 'H**O')) core.print_out(""" Neutral: %6d %6d %6d %6d %6d %6d\n""" % (N, Na, Nb, charge0, mult0, H**O)) core.print_out(""" Cation: %6d %6d %6d %6d %6d\n\n""" % (N - 1, Na1, Nb1, charge1, mult1)) core.print_out(""" => Regula Falsi Iterations <=\n\n""") core.print_out(""" %3s %11s %14s %14s %14s %s\n""" % ('N', 'Omega', 'IP', 'kIP', 'Delta', 'Type')) for k in range(len(omegas)): core.print_out( """ %3d %11.3E %14.6E %14.6E %14.6E %s\n""" % (k + 1, omegas[k], IPs[k], kIPs[k], IPs[k] - kIPs[k], types[k])) optstash.restore() if converged: core.print_out("""\n IP Fitting Converged\n""") core.print_out(""" Final omega = %14.6E\n""" % ((omega_l + omega_r) / 2)) core.print_out( """\n "M,I. does the dying. Fleet just does the flying."\n""") core.print_out(""" -Starship Troopers\n""") else: raise ConvergenceError("""IP Fitting """, step + 1) return ((omega_l + omega_r) / 2)
def run_sf_sapt(name, **kwargs): optstash = p4util.OptionsState(['SCF_TYPE'], ['SCF', 'REFERENCE'], ['SCF', 'DFT_GRAC_SHIFT'], ['SCF', 'SAVE_JK']) core.tstart() # Alter default algorithm if not core.has_global_option_changed('SCF_TYPE'): core.set_global_option('SCF_TYPE', 'DF') core.prepare_options_for_module("SAPT") # Get the molecule of interest ref_wfn = kwargs.get('ref_wfn', None) if ref_wfn is None: sapt_dimer = kwargs.pop('molecule', core.get_active_molecule()) else: core.print_out('Warning! SAPT argument "ref_wfn" is only able to use molecule information.') sapt_dimer = ref_wfn.molecule() sapt_dimer, monomerA, monomerB = proc_util.prepare_sapt_molecule(sapt_dimer, "dimer") # Print out the title and some information core.print_out("\n") core.print_out(" ---------------------------------------------------------\n") core.print_out(" " + "Spin-Flip SAPT Procedure".center(58) + "\n") core.print_out("\n") core.print_out(" " + "by Daniel G. A. Smith and Konrad Patkowski".center(58) + "\n") core.print_out(" ---------------------------------------------------------\n") core.print_out("\n") core.print_out(" ==> Algorithm <==\n\n") core.print_out(" JK Algorithm %12s\n" % core.get_option("SCF", "SCF_TYPE")) core.print_out("\n") core.print_out(" Required computations:\n") core.print_out(" HF (Monomer A)\n") core.print_out(" HF (Monomer B)\n") core.print_out("\n") if (core.get_option('SCF', 'REFERENCE') != 'ROHF'): raise ValidationError('Spin-Flip SAPT currently only supports restricted open-shell references.') # Run the two monomer computations core.IO.set_default_namespace('dimer') data = {} if (core.get_global_option('SCF_TYPE') == 'DF'): core.set_global_option('DF_INTS_IO', 'SAVE') # Compute dimer wavefunction wfn_A = scf_helper("SCF", molecule=monomerA, banner="SF-SAPT: HF Monomer A", **kwargs) core.set_global_option("SAVE_JK", True) wfn_B = scf_helper("SCF", molecule=monomerB, banner="SF-SAPT: HF Monomer B", **kwargs) sapt_jk = wfn_B.jk() core.set_global_option("SAVE_JK", False) core.print_out("\n") core.print_out(" ---------------------------------------------------------\n") core.print_out(" " + "Spin-Flip SAPT Exchange and Electrostatics".center(58) + "\n") core.print_out("\n") core.print_out(" " + "by Daniel G. A. Smith and Konrad Patkowski".center(58) + "\n") core.print_out(" ---------------------------------------------------------\n") core.print_out("\n") sf_data = sapt_sf_terms.compute_sapt_sf(sapt_dimer, sapt_jk, wfn_A, wfn_B) # Print the results core.print_out(" Spin-Flip SAPT Results\n") core.print_out(" " + "-" * 103 + "\n") for key, value in sf_data.items(): value = sf_data[key] print_vals = (key, value * 1000, value * constants.hartree2kcalmol, value * constants.hartree2kJmol) string = " %-26s % 15.8f [mEh] % 15.8f [kcal/mol] % 15.8f [kJ/mol]\n" % print_vals core.print_out(string) core.print_out(" " + "-" * 103 + "\n\n") dimer_wfn = core.Wavefunction.build(sapt_dimer, wfn_A.basisset()) # Set variables psivar_tanslator = { "Elst10": "SAPT ELST ENERGY", "Exch10(S^2) [diagonal]": "SAPT EXCH10(S^2),DIAGONAL ENERGY", "Exch10(S^2) [off-diagonal]": "SAPT EXCH10(S^2),OFF-DIAGONAL ENERGY", "Exch10(S^2) [highspin]": "SAPT EXCH10(S^2),HIGHSPIN ENERGY", } for k, v in sf_data.items(): psi_k = psivar_tanslator[k] dimer_wfn.set_variable(psi_k, v) core.set_variable(psi_k, v) # Copy over highspin core.set_variable("SAPT EXCH ENERGY", sf_data["Exch10(S^2) [highspin]"]) core.tstop() return dimer_wfn
def frac_nuke(name, **kwargs): """Pull all the electrons out, one at a time""" optstash = p4util.OptionsState( ['SCF', 'GUESS'], ['SCF', 'DF_INTS_IO'], ["SCF", "FRAC_START"], ["SCF", "FRAC_RENORMALIZE"], # NYI ["SCF", "FRAC_LOAD"], ["SCF", "FRAC_OCC"], ["SCF", "FRAC_VAL"], ["SCF", "FRAC_DIIS"]) kwargs = p4util.kwargs_lower(kwargs) # Make sure the molecule the user provided is the active one, and neutral molecule = kwargs.pop('molecule', core.get_active_molecule()) molecule.update_geometry() if molecule.molecular_charge() != 0: raise ValidationError( """frac_nuke requires neutral molecule to start.""") if molecule.schoenflies_symbol() != 'c1': core.print_out( """ Requested procedure `frac_nuke` does not make use of molecular symmetry: """ """further calculations in C1 point group.\n""") molecule = molecule.clone() molecule.reset_point_group('c1') molecule.update_geometry() charge0 = molecule.molecular_charge() mult0 = molecule.multiplicity() # By default, we start the frac procedure on the 25th iteration # when not reading a previous guess frac_start = kwargs.get('frac_start', 25) # By default, we occupy by tenths of electrons foccs = kwargs.get('foccs', [1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0]) # By default, H**O and LUMO are both in alpha N = 0 for A in range(molecule.natom()): N += molecule.Z(A) N -= charge0 N = int(N) Nb = int((N - mult0 + 1) / 2) Na = int(N - Nb) charge = charge0 mult = mult0 # By default, nuke all the electrons Nmin = 0 if 'nmax' in kwargs: Nmin = N - int(kwargs['nmax']) # By default, DIIS in FRAC (1.0 occupation is always DIIS'd) frac_diis = kwargs.get('frac_diis', True) # By default, drop the files to the molecule's name root = kwargs.get('filename', molecule.name()) traverse_filename = root + '.traverse.dat' stats_filename = root + '.stats.dat' # => Traverse <= # core.set_local_option("SCF", "DF_INTS_IO", "SAVE") Ns = [] energies = [] potentials = [] convs = [] stats = [] # Run one SCF to burn things in E, wfn = driver.energy('scf', dft_functional=name, return_wfn=True, molecule=molecule, **kwargs) # Determine H**O eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() eps_a.print_out() if Na == Nb: H**O = -Nb elif Nb == 0: H**O = Na else: E_a = eps_a.get(int(Na - 1)) E_b = eps_b.get(int(Nb - 1)) if E_a >= E_b: H**O = Na else: H**O = -Nb stats.append(""" %6d %6d %6d %6d %6d %6d\n""" % (N, Na, Nb, charge, mult, H**O)) if H**O > 0: Na -= 1 else: Nb -= 1 charge += 1 mult = Na - Nb + 1 core.set_local_option("SCF", "DF_INTS_IO", "LOAD") core.set_local_option("SCF", "FRAC_START", frac_start) core.set_local_option("SCF", "FRAC_RENORMALIZE", True) # Nuke 'em Rico! for Nintegral in range(N, Nmin, -1): # Nuke the current H**O for occ in foccs: core.set_local_option("SCF", "FRAC_OCC", [H**O]) core.set_local_option("SCF", "FRAC_VAL", [occ]) E, wfn = driver.energy('scf', dft_functional=name, return_wfn=True, molecule=molecule, **kwargs) C = 1 if E == 0.0: E = core.variable('SCF ITERATION ENERGY') C = 0 if H**O > 0: eps = wfn.epsilon_a() potentials.append(eps.np[H**O - 1]) else: eps = wfn.epsilon_b() potentials.append(eps.np[-H**O - 1]) Ns.append(Nintegral + occ - 1.0) energies.append(E) convs.append(C) core.set_local_option("SCF", "FRAC_START", 2) # NYI core.set_local_option("SCF", "FRAC_LOAD", True) core.set_local_option("SCF", "FRAC_DIIS", frac_diis) core.set_local_option("SCF", "GUESS", "READ") # Set the next charge/mult molecule.set_molecular_charge(charge) molecule.set_multiplicity(mult) # Determine H**O print('DGAS: What ref should this point to?') #ref = core.legacy_wavefunction() eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() if Na == Nb: H**O = -Nb elif Nb == 0: H**O = Na else: E_a = eps_a.np[int(Na - 1)] E_b = eps_b.np[int(Nb - 1)] if E_a >= E_b: H**O = Na else: H**O = -Nb stats.append(""" %6d %6d %6d %6d %6d %6d\n""" % (Nintegral - 1, Na, Nb, charge, mult, H**O)) if H**O > 0: Na -= 1 else: Nb -= 1 charge += 1 mult = Na - Nb + 1 core.set_local_option("SCF", "DF_INTS_IO", "NONE") # => Print the results out <= # E = {} core.print_out("""\n ==> Fractional Occupation Nuke Results <==\n\n""") core.print_out(""" %-11s %-24s %-24s %11s\n""" % ('N', 'Energy', 'H**O Energy', 'Converged')) for k in range(len(Ns)): core.print_out(""" %11.3E %24.16E %24.16E %11d\n""" % (Ns[k], energies[k], potentials[k], convs[k])) E[Ns[k]] = energies[k] core.print_out('\n') core.print_out(""" %6s %6s %6s %6s %6s %6s\n""" % ('N', 'Na', 'Nb', 'Charge', 'Mult', 'H**O')) for line in stats: core.print_out(line) core.print_out( '\n "You shoot a nuke down a bug hole, you got a lot of dead bugs"\n' ) core.print_out(' -Starship Troopers\n') # Drop the files out with open(traverse_filename, 'w') as fh: fh.write(""" %-11s %-24s %-24s %11s\n""" % ('N', 'Energy', 'H**O Energy', 'Converged')) for k in range(len(Ns)): fh.write(""" %11.3E %24.16E %24.16E %11d\n""" % (Ns[k], energies[k], potentials[k], convs[k])) with open(stats_filename, 'w') as fh: fh.write(""" %6s %6s %6s %6s %6s %6s\n""" % ('N', 'Na', 'Nb', 'Charge', 'Mult', 'H**O')) for line in stats: fh.write(line) optstash.restore() return E
def frac_traverse(name, **kwargs): """Scan electron occupancy from +1 electron to -1. Parameters ---------- name : string or function DFT functional string name or function defining functional whose omega is to be optimized. molecule : :ref:`molecule <op_py_molecule>`, optional Target molecule (neutral) for which omega is to be tuned, if not last defined. cation_mult : int, optional Multiplicity of cation, if not neutral multiplicity + 1. anion_mult : int, optional Multiplicity of anion, if not neutral multiplicity + 1. frac_start : int, optional Iteration at which to start frac procedure when not reading previous guess. Defaults to 25. HOMO_occs : list, optional Occupations to step through for cation, by default `[1 - 0.1 * x for x in range(11)]`. LUMO_occs : list, optional Occupations to step through for anion, by default `[1 - 0.1 * x for x in range(11)]`. H**O : int, optional Index of H**O. LUMO : int, optional Index of LUMO. frac_diis : bool, optional Do use DIIS for non-1.0-occupied points? neutral_guess : bool, optional Do use neutral orbitals as guess for the anion? hf_guess: bool, optional Do use UHF guess before UKS? continuous_guess : bool, optional Do carry along guess rather than reguessing at each occupation? filename : str, optional Result filename, if not name of molecule. Returns ------- dict Dictionary associating SCF energies with occupations. """ optstash = p4util.OptionsState( ['SCF', 'GUESS'], ['SCF', 'DF_INTS_IO'], ['SCF', 'REFERENCE'], ["SCF", "FRAC_START"], ["SCF", "FRAC_RENORMALIZE"], #["SCF", "FRAC_LOAD"], ["SCF", "FRAC_OCC"], ["SCF", "FRAC_VAL"], ["SCF", "FRAC_DIIS"]) kwargs = p4util.kwargs_lower(kwargs) # Make sure the molecule the user provided is the active one, and neutral molecule = kwargs.pop('molecule', core.get_active_molecule()) molecule.update_geometry() if molecule.molecular_charge() != 0: raise ValidationError( """frac_traverse requires neutral molecule to start.""") if molecule.schoenflies_symbol() != 'c1': core.print_out( """ Requested procedure `frac_traverse` does not make use of molecular symmetry: """ """further calculations in C1 point group.\n""") molecule = molecule.clone() molecule.reset_point_group('c1') molecule.update_geometry() charge0 = molecule.molecular_charge() mult0 = molecule.multiplicity() chargep = charge0 + 1 chargem = charge0 - 1 multp = kwargs.get('cation_mult', mult0 + 1) multm = kwargs.get('anion_mult', mult0 + 1) # By default, we start the frac procedure on the 25th iteration # when not reading a previous guess frac_start = kwargs.get('frac_start', 25) # By default, we occupy by tenths of electrons HOMO_occs = kwargs.get( 'HOMO_occs', [1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0]) LUMO_occs = kwargs.get( 'LUMO_occs', [1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0]) # By default, H**O and LUMO are both in alpha Z = 0 for A in range(molecule.natom()): Z += molecule.Z(A) Z -= charge0 H**O = kwargs.get('H**O', (Z / 2 + 1 if (Z % 2) else Z / 2)) LUMO = kwargs.get('LUMO', H**O + 1) # By default, DIIS in FRAC (1.0 occupation is always DIIS'd) frac_diis = kwargs.get('frac_diis', True) # By default, use the neutral orbitals as a guess for the anion neutral_guess = kwargs.get('neutral_guess', True) # By default, burn-in with UHF first, if UKS hf_guess = False if core.get_local_option('SCF', 'REFERENCE') == 'UKS': hf_guess = kwargs.get('hf_guess', True) # By default, re-guess at each N continuous_guess = kwargs.get('continuous_guess', False) # By default, drop the files to the molecule's name root = kwargs.get('filename', molecule.name()) traverse_filename = root + '.traverse.dat' # => Traverse <= # occs = [] energies = [] potentials = [] convs = [] # => Run the neutral for its orbitals, if requested <= # core.set_local_option("SCF", "DF_INTS_IO", "SAVE") old_guess = core.get_local_option("SCF", "GUESS") if (neutral_guess): if (hf_guess): core.set_local_option("SCF", "REFERENCE", "UHF") driver.energy('scf', dft_functional=name, molecule=molecule, **kwargs) core.set_local_option("SCF", "GUESS", "READ") core.set_local_option("SCF", "DF_INTS_IO", "LOAD") # => Run the anion first <= # molecule.set_molecular_charge(chargem) molecule.set_multiplicity(multm) # => Burn the anion in with hf, if requested <= # if hf_guess: core.set_local_option("SCF", "REFERENCE", "UHF") driver.energy('scf', dft_functional=name, molecule=molecule, **kwargs) core.set_local_option("SCF", "REFERENCE", "UKS") core.set_local_option("SCF", "GUESS", "READ") core.set_local_option("SCF", "DF_INTS_IO", "SAVE") core.set_local_option("SCF", "FRAC_START", frac_start) core.set_local_option("SCF", "FRAC_RENORMALIZE", True) # NYI core.set_local_option("SCF", "FRAC_LOAD", False) for occ in LUMO_occs: core.set_local_option("SCF", "FRAC_OCC", [LUMO]) core.set_local_option("SCF", "FRAC_VAL", [occ]) E, wfn = driver.energy('scf', dft_functional=name, return_wfn=True, molecule=molecule, **kwargs) C = 1 if E == 0.0: E = core.variable('SCF ITERATION ENERGY') C = 0 if LUMO > 0: eps = wfn.epsilon_a() potentials.append(eps.get(int(LUMO) - 1)) else: eps = wfn.epsilon_b() potentials.append(eps.get(-int(LUMO) - 1)) occs.append(occ) energies.append(E) convs.append(C) core.set_local_option("SCF", "FRAC_START", 2) #core.set_local_option("SCF", "FRAC_LOAD", True) core.set_local_option("SCF", "GUESS", "READ") core.set_local_option("SCF", "FRAC_DIIS", frac_diis) core.set_local_option("SCF", "DF_INTS_IO", "LOAD") # => Run the neutral next <= # molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) # Burn the neutral in with hf, if requested <= # if not continuous_guess: core.set_local_option("SCF", "GUESS", old_guess) if hf_guess: core.set_local_option("SCF", "FRAC_START", 0) core.set_local_option("SCF", "REFERENCE", "UHF") driver.energy('scf', dft_functional=name, molecule=molecule, **kwargs) core.set_local_option("SCF", "REFERENCE", "UKS") core.set_local_option("SCF", "GUESS", "READ") # NYI core.set_local_option("SCF", "FRAC_LOAD", False) core.set_local_option("SCF", "FRAC_START", frac_start) core.set_local_option("SCF", "FRAC_RENORMALIZE", True) for occ in HOMO_occs: core.set_local_option("SCF", "FRAC_OCC", [H**O]) core.set_local_option("SCF", "FRAC_VAL", [occ]) E, wfn = driver.energy('scf', dft_functional=name, return_wfn=True, molecule=molecule, **kwargs) C = 1 if E == 0.0: E = core.variable('SCF ITERATION ENERGY') C = 0 if LUMO > 0: eps = wfn.epsilon_a() potentials.append(eps.get(int(H**O) - 1)) else: eps = wfn.epsilon_b() potentials.append(eps.get(-int(H**O) - 1)) occs.append(occ - 1.0) energies.append(E) convs.append(C) core.set_local_option("SCF", "FRAC_START", 2) # NYI core.set_local_option("SCF", "FRAC_LOAD", True) core.set_local_option("SCF", "GUESS", "READ") core.set_local_option("SCF", "FRAC_DIIS", frac_diis) core.set_local_option("SCF", "DF_INTS_IO", "LOAD") # => Print the results out <= # E = {} core.print_out( """\n ==> Fractional Occupation Traverse Results <==\n\n""") core.print_out(""" %-11s %-24s %-24s %11s\n""" % ('N', 'Energy', 'H**O Energy', 'Converged')) for k in range(len(occs)): core.print_out(""" %11.3E %24.16E %24.16E %11d\n""" % (occs[k], energies[k], potentials[k], convs[k])) E[occs[k]] = energies[k] core.print_out(""" You trying to be a hero Watkins? Just trying to kill some bugs sir! -Starship Troopers""") # Drop the files out with open(traverse_filename, 'w') as fh: fh.write(""" %-11s %-24s %-24s %11s\n""" % ('N', 'Energy', 'H**O Energy', 'Converged')) for k in range(len(occs)): fh.write(""" %11.3E %24.16E %24.16E %11d\n""" % (occs[k], energies[k], potentials[k], convs[k])) optstash.restore() return E
def run_cfour(name, **kwargs): """Function that prepares environment and input files for a calculation calling Stanton and Gauss's CFOUR code. Also processes results back into Psi4 format. This function is not called directly but is instead called by :py:func:`~driver.energy` or :py:func:`~driver.optimize` when a Cfour method is requested (through *name* argument). In order to function correctly, the Cfour executable ``xcfour`` must be present in :envvar:`PATH` or :envvar:`PSIPATH`. .. hlist:: :columns: 1 * Many :ref:`PSI Variables <apdx:cfour_psivar>` extracted from the Cfour output * Python dictionary of associated file constants accessible as ``P4C4_INFO['zmat']``, ``P4C4_INFO['output']``, ``P4C4_INFO['grd']``, *etc.* :type name: string :param name: ``'c4-scf'`` || ``'c4-ccsd(t)'`` || ``'cfour'`` || etc. First argument, usually unlabeled. Indicates the computational method to be applied to the system. :type keep: :ref:`boolean <op_py_boolean>` :param keep: ``'on'`` || |dl| ``'off'`` |dr| Indicates whether to delete the Cfour scratch directory upon completion of the Cfour job. :type path: string :param path: Indicates path to Cfour scratch directory (with respect to Psi4 scratch directory). Otherwise, the default is a subdirectory within the Psi4 scratch directory. If specified, GENBAS and/or ZMAT within will be used. :type genbas: string :param genbas: Indicates that contents should be used for GENBAS file. GENBAS is a complicated topic. It is quite unnecessary if the molecule is from a molecule {...} block and basis is set through |Psifours| BASIS keyword. In that case, a GENBAS is written from LibMints and all is well. Otherwise, a GENBAS is looked for in the usual places: PSIPATH, PATH, PSIDATADIR/basis. If path kwarg is specified, also looks there preferentially for a GENBAS. Can also specify GENBAS within an input file through a string and setting the genbas kwarg. Note that due to the input parser's aggression, blank lines need to be replaced by the text blankline. """ lowername = name.lower() internal_p4c4_info = {} return_wfn = kwargs.pop('return_wfn', False) # Make sure the molecule the user provided is the active one molecule = kwargs.pop('molecule', core.get_active_molecule()) molecule.update_geometry() optstash = p4util.OptionsState(['CFOUR', 'TRANSLATE_PSI4']) # Determine calling function and hence dertype calledby = inspect.stack()[1][3] dertype = ['energy', 'gradient', 'hessian'].index(calledby) #print('I am %s called by %s called by %s.\n' % # (inspect.stack()[0][3], inspect.stack()[1][3], inspect.stack()[2][3])) # Save submission directory current_directory = os.getcwd() # Move into job scratch directory psioh = core.IOManager.shared_object() psio = core.IO.shared_object() os.chdir(psioh.get_default_path()) # Construct and move into cfour subdirectory of job scratch directory cfour_tmpdir = kwargs['path'] if 'path' in kwargs else \ 'psi.' + str(os.getpid()) + '.' + psio.get_default_namespace() + \ '.cfour.' + str(uuid.uuid4())[:8] if not os.path.exists(cfour_tmpdir): os.mkdir(cfour_tmpdir) os.chdir(cfour_tmpdir) # Find environment by merging PSIPATH and PATH environment variables lenv = { 'PATH': ':'.join([os.path.abspath(x) for x in os.environ.get('PSIPATH', '').split(':') if x != '']) + \ ':' + os.environ.get('PATH') + \ ':' + core.get_datadir() + '/basis', 'GENBAS_PATH': core.get_datadir() + '/basis', 'CFOUR_NUM_CORES': os.environ.get('CFOUR_NUM_CORES'), 'MKL_NUM_THREADS': os.environ.get('MKL_NUM_THREADS'), 'OMP_NUM_THREADS': os.environ.get('OMP_NUM_THREADS'), 'LD_LIBRARY_PATH': os.environ.get('LD_LIBRARY_PATH') } if 'path' in kwargs: lenv['PATH'] = kwargs['path'] + ':' + lenv['PATH'] # Filter out None values as subprocess will fault on them lenv = {k: v for k, v in lenv.items() if v is not None} # Load the GENBAS file genbas_path = qcdb.search_file('GENBAS', lenv['GENBAS_PATH']) if genbas_path: try: shutil.copy2(genbas_path, psioh.get_default_path() + cfour_tmpdir) except shutil.Error: # should only fail if src and dest equivalent pass core.print_out("\n GENBAS loaded from %s\n" % (genbas_path)) core.print_out(" CFOUR to be run from %s\n" % (psioh.get_default_path() + cfour_tmpdir)) else: message = """ GENBAS file for CFOUR interface not found. Either: [1] Supply a GENBAS by placing it in PATH or PSIPATH [1a] Use cfour {} block with molecule and basis directives. [1b] Use molecule {} block and CFOUR_BASIS keyword. [2] Allow Psi4's internal basis sets to convert to GENBAS [2a] Use molecule {} block and BASIS keyword. """ core.print_out(message) core.print_out(' Search path that was tried:\n') core.print_out(lenv['PATH'].replace(':', ', ')) # Generate the ZMAT input file in scratch if 'path' in kwargs and os.path.isfile('ZMAT'): core.print_out(" ZMAT loaded from %s\n" % (psioh.get_default_path() + kwargs['path'] + '/ZMAT')) else: with open('ZMAT', 'w') as cfour_infile: cfour_infile.write(write_zmat(lowername, dertype, molecule)) internal_p4c4_info['zmat'] = open('ZMAT', 'r').read() #core.print_out('\n====== Begin ZMAT input for CFOUR ======\n') #core.print_out(open('ZMAT', 'r').read()) #core.print_out('======= End ZMAT input for CFOUR =======\n\n') #print('\n====== Begin ZMAT input for CFOUR ======') #print(open('ZMAT', 'r').read()) #print('======= End ZMAT input for CFOUR =======\n') if 'genbas' in kwargs: with open('GENBAS', 'w') as cfour_basfile: cfour_basfile.write(kwargs['genbas'].replace( '\nblankline\n', '\n\n')) core.print_out(' GENBAS loaded from kwargs string\n') # Close psi4 output file and reopen with filehandle print('output in', current_directory + '/' + core.outfile_name()) pathfill = '' if os.path.isabs( core.outfile_name()) else current_directory + os.path.sep # Handle threading # OMP_NUM_THREADS from env is in lenv from above # threads from psi4 -n (core.get_num_threads()) is ignored # CFOUR_OMP_NUM_THREADS psi4 option takes precedence, handled below if core.has_option_changed('CFOUR', 'CFOUR_OMP_NUM_THREADS') == True: lenv['OMP_NUM_THREADS'] = str( core.get_option('CFOUR', 'CFOUR_OMP_NUM_THREADS')) #print("""\n\n<<<<< RUNNING CFOUR ... >>>>>\n\n""") # Call executable xcfour, directing cfour output to the psi4 output file cfour_executable = kwargs['c4exec'] if 'c4exec' in kwargs else 'xcfour' try: retcode = subprocess.Popen([cfour_executable], bufsize=0, stdout=subprocess.PIPE, env=lenv) except OSError as e: sys.stderr.write( 'Program %s not found in path or execution failed: %s\n' % (cfour_executable, e.strerror)) message = ('Program %s not found in path or execution failed: %s\n' % (cfour_executable, e.strerror)) raise ValidationError(message) c4out = '' while True: data = retcode.stdout.readline() data = data.decode('utf-8') if not data: break core.print_out(data) c4out += data internal_p4c4_info['output'] = c4out c4files = {} core.print_out('\n') for item in ['GRD', 'FCMFINAL', 'DIPOL']: try: with open(psioh.get_default_path() + cfour_tmpdir + '/' + item, 'r') as handle: c4files[item] = handle.read() core.print_out(' CFOUR scratch file %s has been read\n' % (item)) core.print_out('%s\n' % c4files[item]) internal_p4c4_info[item.lower()] = c4files[item] except IOError: pass core.print_out('\n') if molecule.name() == 'blank_molecule_psi4_yo': qcdbmolecule = None else: molecule.update_geometry() qcdbmolecule = qcdb.Molecule( molecule.create_psi4_string_from_molecule()) qcdbmolecule.update_geometry() # c4mol, if it exists, is dinky, just a clue to geometry of cfour results psivar, c4grad, c4mol = qcdb.cfour.harvest(qcdbmolecule, c4out, **c4files) # Absorb results into psi4 data structures for key in psivar.keys(): core.set_variable(key.upper(), float(psivar[key])) if qcdbmolecule is None and c4mol is not None: molecule = geometry(c4mol.create_psi4_string_from_molecule(), name='blank_molecule_psi4_yo') molecule.update_geometry() # This case arises when no Molecule going into calc (cfour {} block) but want # to know the orientation at which grad, properties, etc. are returned (c4mol). # c4mol is dinky, w/o chg, mult, dummies and retains name # blank_molecule_psi4_yo so as to not interfere with future cfour {} blocks if c4grad is not None: mat = core.Matrix.from_list(c4grad) core.set_gradient(mat) #print ' <<< [3] C4-GRD-GRAD >>>' #mat.print() # exit(1) # # Things needed core.so module to do # collect c4out string # read GRD # read FCMFINAL # see if theres an active molecule # # Things delegatable to qcdb # parsing c4out # reading GRD and FCMFINAL strings # reconciling p4 and c4 molecules (orient) # reconciling c4out and GRD and FCMFINAL results # transforming frame of results back to p4 # # Things run_cfour needs to have back # psivar # qcdb.Molecule of c4? # coordinates? # gradient in p4 frame # # Process the cfour output # psivar, c4coord, c4grad = qcdb.cfour.cfour_harvest(c4out) # for key in psivar.keys(): # core.set_variable(key.upper(), float(psivar[key])) # # # Awful Hack - Go Away TODO # if c4grad: # molecule = core.get_active_molecule() # molecule.update_geometry() # # if molecule.name() == 'blank_molecule_psi4_yo': # p4grad = c4grad # p4coord = c4coord # else: # qcdbmolecule = qcdb.Molecule(molecule.create_psi4_string_from_molecule()) # #p4grad = qcdbmolecule.deorient_array_from_cfour(c4coord, c4grad) # #p4coord = qcdbmolecule.deorient_array_from_cfour(c4coord, c4coord) # # with open(psioh.get_default_path() + cfour_tmpdir + '/GRD', 'r') as cfour_grdfile: # c4outgrd = cfour_grdfile.read() # print('GRD\n',c4outgrd) # c4coordGRD, c4gradGRD = qcdb.cfour.cfour_harvest_files(qcdbmolecule, grd=c4outgrd) # # p4mat = core.Matrix.from_list(p4grad) # core.set_gradient(p4mat) # print(' <<< P4 PSIVAR >>>') # for item in psivar: # print(' %30s %16.8f' % (item, psivar[item])) #print(' <<< P4 COORD >>>') #for item in p4coord: # print(' %16.8f %16.8f %16.8f' % (item[0], item[1], item[2])) # print(' <<< P4 GRAD >>>') # for item in c4grad: # print(' %16.8f %16.8f %16.8f' % (item[0], item[1], item[2])) # Clean up cfour scratch directory unless user instructs otherwise keep = yes.match(str(kwargs['keep'])) if 'keep' in kwargs else False os.chdir('..') try: if keep or ('path' in kwargs): core.print_out('\n CFOUR scratch files have been kept in %s\n' % (psioh.get_default_path() + cfour_tmpdir)) else: shutil.rmtree(cfour_tmpdir) except OSError as e: print('Unable to remove CFOUR temporary directory %s' % e, file=sys.stderr) exit(1) # Return to submission directory and reopen output file os.chdir(current_directory) core.print_out('\n') p4util.banner(' Cfour %s %s Results ' % (name.lower(), calledby.capitalize())) core.print_variables() if c4grad is not None: core.get_gradient().print_out() core.print_out('\n') p4util.banner(' Cfour %s %s Results ' % (name.lower(), calledby.capitalize())) core.print_variables() if c4grad is not None: core.get_gradient().print_out() # Quit if Cfour threw error if 'CFOUR ERROR CODE' in core.variables(): raise ValidationError("""Cfour exited abnormally.""") P4C4_INFO.clear() P4C4_INFO.update(internal_p4c4_info) optstash.restore() # new skeleton wavefunction w/mol, highest-SCF basis (just to choose one), & not energy # Feb 2017 hack. Could get proper basis in skel wfn even if not through p4 basis kw gobas = core.get_global_option('BASIS') if core.get_global_option( 'BASIS') else 'sto-3g' basis = core.BasisSet.build(molecule, "ORBITAL", gobas) if basis.has_ECP(): raise ValidationError("""ECPs not hooked up for Cfour""") wfn = core.Wavefunction(molecule, basis) optstash.restore() if dertype == 0: finalquantity = psivar['CURRENT ENERGY'] elif dertype == 1: finalquantity = core.get_gradient() wfn.set_gradient(finalquantity) if finalquantity.rows(0) < 20: core.print_out('CURRENT GRADIENT') finalquantity.print_out() elif dertype == 2: pass #finalquantity = finalhessian #wfn.set_hessian(finalquantity) #if finalquantity.rows(0) < 20: # core.print_out('CURRENT HESSIAN') # finalquantity.print_out() return wfn
def gradient_forte(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that forte can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> gradient('forte') available for : CASSCF """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) # Compute a SCF reference, a wavefunction is return which holds the molecule used, orbitals # Fock matrices, and more ref_wfn = kwargs.get('ref_wfn', None) if ref_wfn is None: ref_wfn = psi4.driver.scf_helper(name, **kwargs) # Get the psi4 option object optstash = p4util.OptionsState(['GLOBALS', 'DERTYPE']) psi4_options = psi4.core.get_options() psi4_options.set_current_module('FORTE') # Get the forte option object options = forte.forte_options options.get_options_from_psi4(psi4_options) if ('DF' in options.get_str('INT_TYPE')): raise Exception('analytic gradient is not implemented for density fitting') if (options.get_str('MINAO_BASIS')): minao_basis = psi4.core.BasisSet.build(ref_wfn.molecule(), 'MINAO_BASIS', options.get_str('MINAO_BASIS')) ref_wfn.set_basisset('MINAO_BASIS', minao_basis) # Start Forte, initialize ambit my_proc_n_nodes = forte.startup() my_proc, n_nodes = my_proc_n_nodes # Print the banner forte.banner() # Create the MOSpaceInfo object mo_space_info = forte.make_mo_space_info(ref_wfn, options) # Call methods that project the orbitals (AVAS, embedding) mo_space_info = orbital_projection(ref_wfn, options, mo_space_info) state = forte.make_state_info_from_psi_wfn(ref_wfn) scf_info = forte.SCFInfo(ref_wfn) state_weights_map = forte.make_state_weights_map(options,ref_wfn) # Run a method job_type = options.get_str('JOB_TYPE') energy = 0.0 if not job_type == 'CASSCF': raise Exception('analytic gradient is only implemented for CASSCF') start = time.time() # Make an integral object ints = forte.make_forte_integrals(ref_wfn, options, mo_space_info) # Rotate orbitals before computation orb_type = options.get_str("ORBITAL_TYPE") if orb_type != 'CANONICAL': orb_t = forte.make_orbital_transformation(orb_type, scf_info, options, ints, mo_space_info) orb_t.compute_transformation() Ua = orb_t.get_Ua() Ub = orb_t.get_Ub() ints.rotate_orbitals(Ua,Ub) # Run gradient computation energy = forte.forte_old_methods(ref_wfn, options, ints, mo_space_info) derivobj = psi4.core.Deriv(ref_wfn) derivobj.set_deriv_density_backtransformed(True) derivobj.set_ignore_reference(True) grad = derivobj.compute() #psi4.core.DerivCalcType.Correlated ref_wfn.set_gradient(grad) optstash.restore() end = time.time() #print('\n\n Your calculation took ', (end - start), ' seconds'); # Close ambit, etc. forte.cleanup() return ref_wfn
def database(name, db_name, **kwargs): r"""Function to access the molecule objects and reference energies of popular chemical databases. :aliases: db() :returns: (*float*) Mean absolute deviation of the database in kcal/mol :PSI variables: .. hlist:: :columns: 1 * :psivar:`db_name DATABASE MEAN SIGNED DEVIATION <db_nameDATABASEMEANSIGNEDDEVIATION>` * :psivar:`db_name DATABASE MEAN ABSOLUTE DEVIATION <db_nameDATABASEMEANABSOLUTEDEVIATION>` * :psivar:`db_name DATABASE ROOT-MEAN-SQUARE DEVIATION <db_nameDATABASEROOT-MEAN-SQUARESIGNEDDEVIATION>` * Python dictionaries of results accessible as ``DB_RGT`` and ``DB_RXN``. .. note:: It is very easy to make a database from a collection of xyz files using the script :source:`share/scripts/ixyz2database.py`. See :ref:`sec:createDatabase` for details. .. caution:: Some features are not yet implemented. Buy a developer some coffee. - In sow/reap mode, use only global options (e.g., the local option set by ``set scf scf_type df`` will not be respected). .. note:: To access a database that is not embedded in a |PSIfour| distribution, add the path to the directory containing the database to the environment variable :envvar:`PYTHONPATH`. :type name: string :param name: ``'scf'`` || ``'sapt0'`` || ``'ccsd(t)'`` || etc. First argument, usually unlabeled. Indicates the computational method to be applied to the database. May be any valid argument to :py:func:`~driver.energy`. :type db_name: string :param db_name: ``'BASIC'`` || ``'S22'`` || ``'HTBH'`` || etc. Second argument, usually unlabeled. Indicates the requested database name, matching (case insensitive) the name of a python file in ``psi4/share/databases`` or :envvar:`PYTHONPATH`. Consult that directory for available databases and literature citations. :type func: :ref:`function <op_py_function>` :param func: |dl| ``energy`` |dr| || ``optimize`` || ``cbs`` Indicates the type of calculation to be performed on each database member. The default performs a single-point ``energy('name')``, while ``optimize`` perfoms a geometry optimization on each reagent, and ``cbs`` performs a compound single-point energy. If a nested series of python functions is intended (see :ref:`sec:intercalls`), use keyword ``db_func`` instead of ``func``. :type mode: string :param mode: |dl| ``'continuous'`` |dr| || ``'sow'`` || ``'reap'`` Indicates whether the calculations required to complete the database are to be run in one file (``'continuous'``) or are to be farmed out in an embarrassingly parallel fashion (``'sow'``/``'reap'``). For the latter, run an initial job with ``'sow'`` and follow instructions in its output file. :type cp: :ref:`boolean <op_py_boolean>` :param cp: ``'on'`` || |dl| ``'off'`` |dr| Indicates whether counterpoise correction is employed in computing interaction energies. Use this option and NOT the :py:func:`~wrappers.cp` function for BSSE correction in database(). Option available (See :ref:`sec:availableDatabases`) only for databases of bimolecular complexes. :type rlxd: :ref:`boolean <op_py_boolean>` :param rlxd: ``'on'`` || |dl| ``'off'`` |dr| Indicates whether correction for deformation energy is employed in computing interaction energies. Option available (See :ref:`sec:availableDatabases`) only for databases of bimolecular complexes with non-frozen monomers, e.g., HBC6. :type symm: :ref:`boolean <op_py_boolean>` :param symm: |dl| ``'on'`` |dr| || ``'off'`` Indicates whether the native symmetry of the database reagents is employed (``'on'``) or whether it is forced to :math:`C_1` symmetry (``'off'``). Some computational methods (e.g., SAPT) require no symmetry, and this will be set by database(). :type zpe: :ref:`boolean <op_py_boolean>` :param zpe: ``'on'`` || |dl| ``'off'`` |dr| Indicates whether zero-point-energy corrections are appended to single-point energy values. Option valid only for certain thermochemical databases. Disabled until Hessians ready. :type benchmark: string :param benchmark: |dl| ``'default'`` |dr| || ``'S22A'`` || etc. Indicates whether a non-default set of reference energies, if available (See :ref:`sec:availableDatabases`), are employed for the calculation of error statistics. :type tabulate: array of strings :param tabulate: |dl| ``[]`` |dr| || ``['scf total energy', 'natom']`` || etc. Indicates whether to form tables of variables other than the primary requested energy. Available for any PSI variable. :type subset: string or array of strings :param subset: Indicates a subset of the full database to run. This is a very flexible option and can be used in three distinct ways, outlined below. Note that two take a string and the last takes an array. See `Available Databases`_ for available values. * ``'small'`` || ``'large'`` || ``'equilibrium'`` Calls predefined subsets of the requested database, either ``'small'``, a few of the smallest database members, ``'large'``, the largest of the database members, or ``'equilibrium'``, the equilibrium geometries for a database composed of dissociation curves. * ``'BzBz_S'`` || ``'FaOOFaON'`` || ``'ArNe'`` || ``'HB'`` || etc. For databases composed of dissociation curves, or otherwise divided into subsets, individual curves and subsets can be called by name. Consult the database python files for available molecular systems (case insensitive). * ``[1,2,5]`` || ``['1','2','5']`` || ``['BzMe-3.5', 'MeMe-5.0']`` || etc. Specify a list of database members to run. Consult the database python files for available molecular systems. This is the only portion of database input that is case sensitive; choices for this keyword must match the database python file. :examples: >>> # [1] Two-stage SCF calculation on short, equilibrium, and long helium dimer >>> db('scf','RGC10',cast_up='sto-3g',subset=['HeHe-0.85','HeHe-1.0','HeHe-1.5'], tabulate=['scf total energy','natom']) >>> # [2] Counterpoise-corrected interaction energies for three complexes in S22 >>> # Error statistics computed wrt an old benchmark, S22A >>> database('mp2','S22',cp=1,subset=[16,17,8],benchmark='S22A') >>> # [3] SAPT0 on the neon dimer dissociation curve >>> db('sapt0',subset='NeNe',cp=0,symm=0,db_name='RGC10') >>> # [4] Optimize system 1 in database S22, producing tables of scf and mp2 energy >>> db('mp2','S22',db_func=optimize,subset=[1], tabulate=['mp2 total energy','current energy']) >>> # [5] CCSD on the smallest systems of HTBH, a hydrogen-transfer database >>> database('ccsd','HTBH',subset='small', tabulate=['ccsd total energy', 'mp2 total energy']) """ lowername = name #TODO kwargs = p4util.kwargs_lower(kwargs) # Wrap any positional arguments into kwargs (for intercalls among wrappers) if not ('name' in kwargs) and name: kwargs['name'] = name #.lower() if not ('db_name' in kwargs) and db_name: kwargs['db_name'] = db_name # Establish function to call func = kwargs.pop('db_func', kwargs.pop('func', energy)) kwargs['db_func'] = func # Bounce to CP if bsse kwarg (someday) if kwargs.get('bsse_type', None) is not None: raise ValidationError( """Database: Cannot specify bsse_type for database. Use the cp keyword withing database instead.""" ) allowoptexceeded = kwargs.get('allowoptexceeded', False) optstash = p4util.OptionsState(['WRITER_FILE_LABEL'], ['SCF', 'REFERENCE']) # Wrapper wholly defines molecule. discard any passed-in kwargs.pop('molecule', None) # Paths to search for database files: here + PSIPATH + library + PYTHONPATH db_paths = [] db_paths.append(os.getcwd()) db_paths.extend(os.environ.get('PSIPATH', '').split(os.path.pathsep)) db_paths.append(os.path.join(core.get_datadir(), 'databases')) db_paths.append(os.path.dirname(__file__)) db_paths = list(map(os.path.abspath, db_paths)) sys.path[1:1] = db_paths # TODO this should be modernized a la interface_cfour # Define path and load module for requested database database = p4util.import_ignorecase(db_name) if database is None: core.print_out('\nPython module for database %s failed to load\n\n' % (db_name)) core.print_out('\nSearch path that was tried:\n') core.print_out(", ".join(map(str, sys.path))) raise ValidationError("Python module loading problem for database " + str(db_name)) else: dbse = database.dbse HRXN = database.HRXN ACTV = database.ACTV RXNM = database.RXNM BIND = database.BIND TAGL = database.TAGL GEOS = database.GEOS try: DATA = database.DATA except AttributeError: DATA = {} user_writer_file_label = core.get_global_option('WRITER_FILE_LABEL') user_reference = core.get_global_option('REFERENCE') # Configuration based upon e_name & db_name options # Force non-supramolecular if needed if not hasattr(lowername, '__call__') and re.match(r'^.*sapt', lowername): try: database.ACTV_SA except AttributeError: raise ValidationError( 'Database %s not suitable for non-supramolecular calculation.' % (db_name)) else: ACTV = database.ACTV_SA # Force open-shell if needed openshell_override = 0 if user_reference in ['RHF', 'RKS']: try: database.isOS except AttributeError: pass else: if p4util.yes.match(str(database.isOS)): openshell_override = 1 core.print_out( '\nSome reagents in database %s require an open-shell reference; will be reset to UHF/UKS as needed.\n' % (db_name)) # Configuration based upon database keyword options # Option symmetry- whether symmetry treated normally or turned off (currently req'd for dfmp2 & dft) db_symm = kwargs.get('symm', True) symmetry_override = 0 if db_symm is False: symmetry_override = 1 elif db_symm is True: pass else: raise ValidationError("""Symmetry mode '%s' not valid.""" % (db_symm)) # Option mode of operation- whether db run in one job or files farmed out db_mode = kwargs.pop('db_mode', kwargs.pop('mode', 'continuous')).lower() kwargs['db_mode'] = db_mode if db_mode == 'continuous': pass elif db_mode == 'sow': pass elif db_mode == 'reap': db_linkage = kwargs.get('linkage', None) if db_linkage is None: raise ValidationError( """Database execution mode 'reap' requires a linkage option.""" ) else: raise ValidationError("""Database execution mode '%s' not valid.""" % (db_mode)) # Option counterpoise- whether for interaction energy databases run in bsse-corrected or not db_cp = kwargs.get('cp', False) if db_cp is True: try: database.ACTV_CP except AttributeError: raise ValidationError( """Counterpoise correction mode 'yes' invalid for database %s.""" % (db_name)) else: ACTV = database.ACTV_CP elif db_cp is False: pass else: raise ValidationError( """Counterpoise correction mode '%s' not valid.""" % (db_cp)) # Option relaxed- whether for non-frozen-monomer interaction energy databases include deformation correction or not? db_rlxd = kwargs.get('rlxd', False) if db_rlxd is True: if db_cp is True: try: database.ACTV_CPRLX database.RXNM_CPRLX except AttributeError: raise ValidationError( 'Deformation and counterpoise correction mode \'yes\' invalid for database %s.' % (db_name)) else: ACTV = database.ACTV_CPRLX RXNM = database.RXNM_CPRLX elif db_cp is False: try: database.ACTV_RLX except AttributeError: raise ValidationError( 'Deformation correction mode \'yes\' invalid for database %s.' % (db_name)) else: ACTV = database.ACTV_RLX elif db_rlxd is False: #elif no.match(str(db_rlxd)): pass else: raise ValidationError('Deformation correction mode \'%s\' not valid.' % (db_rlxd)) # Option zero-point-correction- whether for thermochem databases jobs are corrected by zpe db_zpe = kwargs.get('zpe', False) if db_zpe is True: raise ValidationError( 'Zero-point-correction mode \'yes\' not yet implemented.') elif db_zpe is False: pass else: raise ValidationError('Zero-point-correction \'mode\' %s not valid.' % (db_zpe)) # Option benchmark- whether error statistics computed wrt alternate reference energies db_benchmark = 'default' if 'benchmark' in kwargs: db_benchmark = kwargs['benchmark'] if db_benchmark.lower() == 'default': pass else: BIND = p4util.getattr_ignorecase(database, 'BIND_' + db_benchmark) if BIND is None: raise ValidationError( 'Special benchmark \'%s\' not available for database %s.' % (db_benchmark, db_name)) # Option tabulate- whether tables of variables other than primary energy method are formed # TODO db(func=cbs,tabulate=[non-current-energy]) # broken db_tabulate = [] if 'tabulate' in kwargs: db_tabulate = kwargs['tabulate'] # Option subset- whether all of the database or just a portion is run db_subset = HRXN if 'subset' in kwargs: db_subset = kwargs['subset'] if isinstance(db_subset, (str, bytes)): if db_subset.lower() == 'small': try: database.HRXN_SM except AttributeError: raise ValidationError( """Special subset 'small' not available for database %s.""" % (db_name)) else: HRXN = database.HRXN_SM elif db_subset.lower() == 'large': try: database.HRXN_LG except AttributeError: raise ValidationError( """Special subset 'large' not available for database %s.""" % (db_name)) else: HRXN = database.HRXN_LG elif db_subset.lower() == 'equilibrium': try: database.HRXN_EQ except AttributeError: raise ValidationError( """Special subset 'equilibrium' not available for database %s.""" % (db_name)) else: HRXN = database.HRXN_EQ else: HRXN = p4util.getattr_ignorecase(database, db_subset) if HRXN is None: HRXN = p4util.getattr_ignorecase(database, 'HRXN_' + db_subset) if HRXN is None: raise ValidationError( """Special subset '%s' not available for database %s.""" % (db_subset, db_name)) else: temp = [] for rxn in db_subset: if rxn in HRXN: temp.append(rxn) else: raise ValidationError( """Subset element '%s' not a member of database %s.""" % (str(rxn), db_name)) HRXN = temp temp = [] for rxn in HRXN: temp.append(ACTV['%s-%s' % (dbse, rxn)]) HSYS = p4util.drop_duplicates(sum(temp, [])) # Sow all the necessary reagent computations core.print_out("\n\n") p4util.banner(("Database %s Computation" % (db_name))) core.print_out("\n") # write index of calcs to output file instructions = """\n The database single-job procedure has been selected through mode='continuous'.\n""" instructions += """ Calculations for the reagents will proceed in the order below and will be followed\n""" instructions += """ by summary results for the database.\n\n""" for rgt in HSYS: instructions += """ %-s\n""" % (rgt) core.print_out(instructions) # Loop through chemical systems ERGT = {} ERXN = {} VRGT = {} VRXN = {} for rgt in HSYS: VRGT[rgt] = {} core.print_out('\n') p4util.banner(' Database {} Computation: Reagent {} \n {}'.format( db_name, rgt, TAGL[rgt])) core.print_out('\n') molecule = core.Molecule.from_dict(GEOS[rgt].to_dict()) molecule.set_name(rgt) molecule.update_geometry() if symmetry_override: molecule.reset_point_group('c1') molecule.fix_orientation(True) molecule.fix_com(True) molecule.update_geometry() if (openshell_override) and (molecule.multiplicity() != 1): if user_reference == 'RHF': core.set_global_option('REFERENCE', 'UHF') elif user_reference == 'RKS': core.set_global_option('REFERENCE', 'UKS') core.set_global_option( 'WRITER_FILE_LABEL', user_writer_file_label + ('' if user_writer_file_label == '' else '-') + rgt) if allowoptexceeded: try: ERGT[rgt] = func(molecule=molecule, **kwargs) except ConvergenceError: core.print_out(f"Optimization exceeded cycles for {rgt}") ERGT[rgt] = 0.0 else: ERGT[rgt] = func(molecule=molecule, **kwargs) core.print_variables() core.print_out(" Database Contributions Map:\n {}\n".format('-' * 75)) for rxn in HRXN: db_rxn = dbse + '-' + str(rxn) if rgt in ACTV[db_rxn]: core.print_out( ' reagent {} contributes by {:.4f} to reaction {}\n'. format(rgt, RXNM[db_rxn][rgt], db_rxn)) core.print_out('\n') for envv in db_tabulate: VRGT[rgt][envv.upper()] = core.variable(envv) core.set_global_option("REFERENCE", user_reference) core.clean() #core.opt_clean() core.clean_variables() # Reap all the necessary reaction computations core.print_out("\n") p4util.banner(("Database %s Results" % (db_name))) core.print_out("\n") maxactv = [] for rxn in HRXN: maxactv.append(len(ACTV[dbse + '-' + str(rxn)])) maxrgt = max(maxactv) table_delimit = '-' * (62 + 20 * maxrgt) tables = '' # find any reactions that are incomplete FAIL = collections.defaultdict(int) for rxn in HRXN: db_rxn = dbse + '-' + str(rxn) for i in range(len(ACTV[db_rxn])): if abs(ERGT[ACTV[db_rxn][i]]) < 1.0e-12: if not allowoptexceeded: FAIL[rxn] = 1 # tabulate requested process::environment variables tables += """ For each VARIABLE requested by tabulate, a 'Reaction Value' will be formed from\n""" tables += """ 'Reagent' values according to weightings 'Wt', as for the REQUESTED ENERGY below.\n""" tables += """ Depending on the nature of the variable, this may or may not make any physical sense.\n""" for rxn in HRXN: db_rxn = dbse + '-' + str(rxn) VRXN[db_rxn] = {} for envv in db_tabulate: envv = envv.upper() tables += """\n ==> %s <==\n\n""" % (envv.title()) tables += _tblhead(maxrgt, table_delimit, 2) for rxn in HRXN: db_rxn = dbse + '-' + str(rxn) if FAIL[rxn]: tables += """\n%23s %8s %8s %8s %8s""" % (db_rxn, '', '****', '', '') for i in range(len(ACTV[db_rxn])): tables += """ %16.8f %2.0f""" % (VRGT[ ACTV[db_rxn][i]][envv], RXNM[db_rxn][ACTV[db_rxn][i]]) else: VRXN[db_rxn][envv] = 0.0 for i in range(len(ACTV[db_rxn])): VRXN[db_rxn][envv] += VRGT[ ACTV[db_rxn][i]][envv] * RXNM[db_rxn][ACTV[db_rxn][i]] tables += """\n%23s %16.8f """ % ( db_rxn, VRXN[db_rxn][envv]) for i in range(len(ACTV[db_rxn])): tables += """ %16.8f %2.0f""" % (VRGT[ ACTV[db_rxn][i]][envv], RXNM[db_rxn][ACTV[db_rxn][i]]) tables += """\n %s\n""" % (table_delimit) # tabulate primary requested energy variable with statistics count_rxn = 0 minDerror = 100000.0 maxDerror = 0.0 MSDerror = 0.0 MADerror = 0.0 RMSDerror = 0.0 tables += """\n ==> %s <==\n\n""" % ('Requested Energy') tables += _tblhead(maxrgt, table_delimit, 1) for rxn in HRXN: db_rxn = dbse + '-' + str(rxn) if FAIL[rxn]: tables += """\n%23s %8.4f %8s %10s %10s""" % ( db_rxn, BIND[db_rxn], '****', '****', '****') for i in range(len(ACTV[db_rxn])): tables += """ %16.8f %2.0f""" % (ERGT[ACTV[db_rxn][i]], RXNM[db_rxn][ACTV[db_rxn][i]]) else: ERXN[db_rxn] = 0.0 for i in range(len(ACTV[db_rxn])): ERXN[db_rxn] += ERGT[ACTV[db_rxn][i]] * RXNM[db_rxn][ ACTV[db_rxn][i]] error = constants.hartree2kcalmol * ERXN[db_rxn] - BIND[db_rxn] tables += """\n%23s %8.4f %8.4f %10.4f %10.4f""" % ( db_rxn, BIND[db_rxn], constants.hartree2kcalmol * ERXN[db_rxn], error, error * constants.cal2J) for i in range(len(ACTV[db_rxn])): tables += """ %16.8f %2.0f""" % (ERGT[ACTV[db_rxn][i]], RXNM[db_rxn][ACTV[db_rxn][i]]) if abs(error) < abs(minDerror): minDerror = error if abs(error) > abs(maxDerror): maxDerror = error MSDerror += error MADerror += abs(error) RMSDerror += error * error count_rxn += 1 tables += """\n %s\n""" % (table_delimit) if count_rxn: MSDerror /= float(count_rxn) MADerror /= float(count_rxn) RMSDerror = math.sqrt(RMSDerror / float(count_rxn)) tables += """%23s %19s %10.4f %10.4f\n""" % ( 'Minimal Dev', '', minDerror, minDerror * constants.cal2J) tables += """%23s %19s %10.4f %10.4f\n""" % ( 'Maximal Dev', '', maxDerror, maxDerror * constants.cal2J) tables += """%23s %19s %10.4f %10.4f\n""" % ( 'Mean Signed Dev', '', MSDerror, MSDerror * constants.cal2J) tables += """%23s %19s %10.4f %10.4f\n""" % ( 'Mean Absolute Dev', '', MADerror, MADerror * constants.cal2J) tables += """%23s %19s %10.4f %10.4f\n""" % ( 'RMS Dev', '', RMSDerror, RMSDerror * constants.cal2J) tables += """ %s\n""" % (table_delimit) core.set_variable('%s DATABASE MEAN SIGNED DEVIATION' % (db_name), MSDerror) core.set_variable('%s DATABASE MEAN ABSOLUTE DEVIATION' % (db_name), MADerror) core.set_variable('%s DATABASE ROOT-MEAN-SQUARE DEVIATION' % (db_name), RMSDerror) core.print_out(tables) finalenergy = MADerror else: finalenergy = 0.0 optstash.restore() DB_RGT.clear() DB_RGT.update(VRGT) DB_RXN.clear() DB_RXN.update(VRXN) return finalenergy
def run_v2rdm_casscf(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that v2rdm_casscf can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('v2rdm_casscf') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) optstash = p4util.OptionsState(['SCF', 'DF_INTS_IO']) psi4.core.set_local_option('SCF', 'DF_INTS_IO', 'SAVE') # Your plugin's psi4 run sequence goes here ref_wfn = kwargs.get('ref_wfn', None) if ref_wfn is None: ref_wfn = psi4.driver.scf_helper(name, **kwargs) # if restarting from a checkpoint file, this file # needs to be in scratch with the correct name filename = psi4.core.get_option("V2RDM_CASSCF", "RESTART_FROM_CHECKPOINT_FILE") # Ensure IWL files have been written when not using DF/CD scf_type = psi4.core.get_option('SCF', 'SCF_TYPE') if (scf_type == 'PK' or scf_type == 'DIRECT'): proc_util.check_iwl_file_from_scf_type( psi4.core.get_option('SCF', 'SCF_TYPE'), ref_wfn) # reorder wavefuntions based on user input # apply a list of 2x2 rotation matrices to the orbitals in the form of [irrep, orbital1, orbital2, theta] # where an angle of 0 would do nothing and an angle of 90 would switch the two orbitals. # the indices of irreps and orbitals start from 0 reorder_orbitals = psi4.core.get_option("V2RDM_CASSCF", "MCSCF_ROTATE") for orbord in reorder_orbitals: if type(orbord) != list: raise psi4.p4util.PsiException( "Each element of the orbtial rotate list requires 4 arguements (irrep, orb1, orb2, theta)." ) if len(orbord) != 4: raise psi4.p4util.PsiException( "Each element of the orbtial rotate list requires 4 arguements (irrep, orb1, orb2, theta)." ) irrep, orb1, orb2, theta = orbord if irrep > ref_wfn.Ca().nirrep(): raise psi4.p4util.PsiException( "REORDER_ORBITALS: Expression %s irrep number is larger than the number of irreps" % (str(orbord))) if max(orb1, orb2) > ref_wfn.Ca().coldim()[irrep]: raise psi4.p4util.PsiException( "REORDER_ORBITALS: Expression %s orbital number exceeds number of orbitals in irrep" % (str(orbord))) theta = numpy.deg2rad(theta) x_a = ref_wfn.Ca().nph[irrep][:, orb1].copy() y_a = ref_wfn.Ca().nph[irrep][:, orb2].copy() xp_a = numpy.cos(theta) * x_a - numpy.sin(theta) * y_a yp_a = numpy.sin(theta) * x_a + numpy.cos(theta) * y_a ref_wfn.Ca().nph[irrep][:, orb1] = xp_a ref_wfn.Ca().nph[irrep][:, orb2] = yp_a x_b = ref_wfn.Ca().nph[irrep][:, orb1].copy() y_b = ref_wfn.Ca().nph[irrep][:, orb2].copy() xp_b = numpy.cos(theta) * x_b - numpy.sin(theta) * y_b yp_b = numpy.sin(theta) * x_b + numpy.cos(theta) * y_b ref_wfn.Ca().nph[irrep][:, orb1] = xp_b ref_wfn.Ca().nph[irrep][:, orb2] = yp_b returnvalue = psi4.core.plugin('v2rdm_casscf.so', ref_wfn) optstash.restore() return returnvalue
def run_sapt_dft(name, **kwargs): optstash = p4util.OptionsState(['SCF', 'SCF_TYPE'], ['SCF', 'REFERENCE'], ['SCF', 'DFT_FUNCTIONAL'], ['SCF', 'DFT_GRAC_SHIFT'], ['SCF', 'SAVE_JK']) core.tstart() # Alter default algorithm if not core.has_option_changed('SCF', 'SCF_TYPE'): core.set_local_option('SCF', 'SCF_TYPE', 'DF') core.prepare_options_for_module("SAPT") # Get the molecule of interest ref_wfn = kwargs.get('ref_wfn', None) if ref_wfn is None: sapt_dimer = kwargs.pop('molecule', core.get_active_molecule()) else: core.print_out( 'Warning! SAPT argument "ref_wfn" is only able to use molecule information.' ) sapt_dimer = ref_wfn.molecule() # Shifting to C1 so we need to copy the active molecule if sapt_dimer.schoenflies_symbol() != 'c1': core.print_out( ' SAPT does not make use of molecular symmetry, further calculations in C1 point group.\n' ) # Make sure the geometry doesnt shift or rotate sapt_dimer = sapt_dimer.clone() sapt_dimer.reset_point_group('c1') sapt_dimer.fix_orientation(True) sapt_dimer.fix_com(True) sapt_dimer.update_geometry() # Grab overall settings mon_a_shift = core.get_option("SAPT", "SAPT_DFT_GRAC_SHIFT_A") mon_b_shift = core.get_option("SAPT", "SAPT_DFT_GRAC_SHIFT_B") do_delta_hf = core.get_option("SAPT", "SAPT_DFT_DO_DHF") sapt_dft_functional = core.get_option("SAPT", "SAPT_DFT_FUNCTIONAL") # Print out the title and some information core.print_out("\n") core.print_out( " ---------------------------------------------------------\n") core.print_out(" " + "SAPT(DFT) Procedure".center(58) + "\n") core.print_out("\n") core.print_out(" " + "by Daniel G. A. Smith".center(58) + "\n") core.print_out( " ---------------------------------------------------------\n") core.print_out("\n") core.print_out(" ==> Algorithm <==\n\n") core.print_out(" SAPT DFT Functional %12s\n" % str(sapt_dft_functional)) core.print_out(" Monomer A GRAC Shift %12.6f\n" % mon_a_shift) core.print_out(" Monomer B GRAC Shift %12.6f\n" % mon_b_shift) core.print_out(" Delta HF %12s\n" % ("True" if do_delta_hf else "False")) core.print_out(" JK Algorithm %12s\n" % core.get_option("SCF", "SCF_TYPE")) core.print_out("\n") core.print_out(" Required computations:\n") if (do_delta_hf): core.print_out(" HF (Dimer)\n") core.print_out(" HF (Monomer A)\n") core.print_out(" HF (Monomer B)\n") core.print_out(" DFT (Monomer A)\n") core.print_out(" DFT (Monomer B)\n") core.print_out("\n") if (mon_a_shift == 0.0) or (mon_b_shift == 0.0): raise ValidationError( 'SAPT(DFT): must set both "SAPT_DFT_GRAC_SHIFT_A" and "B".') if (core.get_option('SCF', 'REFERENCE') != 'RHF'): raise ValidationError( 'SAPT(DFT) currently only supports restricted references.') nfrag = sapt_dimer.nfragments() if nfrag != 2: raise ValidationError( 'SAPT requires active molecule to have 2 fragments, not %s.' % (nfrag)) monomerA = sapt_dimer.extract_subsets(1, 2) monomerA.set_name('monomerA') monomerB = sapt_dimer.extract_subsets(2, 1) monomerB.set_name('monomerB') core.IO.set_default_namespace('dimer') data = {} core.set_global_option("SAVE_JK", True) if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): # core.set_global_option('DF_INTS_IO', 'LOAD') core.set_global_option('DF_INTS_IO', 'SAVE') # # Compute dimer wavefunction hf_cache = {} hf_wfn_dimer = None if do_delta_hf: if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.set_global_option('DF_INTS_IO', 'SAVE') hf_data = {} hf_wfn_dimer = scf_helper("SCF", molecule=sapt_dimer, banner="SAPT(DFT): delta HF Dimer", **kwargs) hf_data["HF DIMER"] = core.get_variable("CURRENT ENERGY") if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.IO.change_file_namespace(97, 'dimer', 'monomerA') hf_wfn_A = scf_helper("SCF", molecule=monomerA, banner="SAPT(DFT): delta HF Monomer A", **kwargs) hf_data["HF MONOMER A"] = core.get_variable("CURRENT ENERGY") if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.IO.change_file_namespace(97, 'monomerA', 'monomerB') hf_wfn_B = scf_helper("SCF", molecule=monomerB, banner="SAPT(DFT): delta HF Monomer B", **kwargs) hf_data["HF MONOMER B"] = core.get_variable("CURRENT ENERGY") # Move it back to monomer A if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.IO.change_file_namespace(97, 'monomerB', 'dimer') core.print_out("\n") core.print_out( " ---------------------------------------------------------\n" ) core.print_out(" " + "SAPT(DFT): delta HF Segement".center(58) + "\n") core.print_out("\n") core.print_out(" " + "by Daniel G. A. Smith and Rob Parrish".center(58) + "\n") core.print_out( " ---------------------------------------------------------\n" ) core.print_out("\n") # Build cache and JK sapt_jk = hf_wfn_B.jk() hf_cache = sapt_jk_terms.build_sapt_jk_cache(hf_wfn_A, hf_wfn_B, sapt_jk, True) # Electostatics elst = sapt_jk_terms.electrostatics(hf_cache, True) hf_data.update(elst) # Exchange exch = sapt_jk_terms.exchange(hf_cache, sapt_jk, True) hf_data.update(exch) # Induction ind = sapt_jk_terms.induction( hf_cache, sapt_jk, True, maxiter=core.get_option("SAPT", "MAXITER"), conv=core.get_option("SAPT", "D_CONVERGENCE")) hf_data.update(ind) dhf_value = hf_data["HF DIMER"] - hf_data["HF MONOMER A"] - hf_data[ "HF MONOMER B"] core.print_out("\n") core.print_out( print_sapt_hf_summary(hf_data, "SAPT(HF)", delta_hf=dhf_value)) data["Delta HF Correction"] = core.get_variable("SAPT(DFT) Delta HF") if hf_wfn_dimer is None: dimer_wfn = core.Wavefunction.build(sapt_dimer, core.get_global_option("BASIS")) else: dimer_wfn = hf_wfn_dimer # Set the primary functional core.set_global_option("DFT_FUNCTIONAL", core.get_option("SAPT", "SAPT_DFT_FUNCTIONAL")) core.set_local_option('SCF', 'REFERENCE', 'RKS') # Compute Monomer A wavefunction if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.IO.change_file_namespace(97, 'dimer', 'monomerA') if mon_a_shift: core.set_global_option("DFT_GRAC_SHIFT", mon_a_shift) # Save the JK object core.IO.set_default_namespace('monomerA') wfn_A = scf_helper("SCF", molecule=monomerA, banner="SAPT(DFT): DFT Monomer A", **kwargs) data["DFT MONOMERA"] = core.get_variable("CURRENT ENERGY") core.set_global_option("DFT_GRAC_SHIFT", 0.0) # Compute Monomer B wavefunction if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.IO.change_file_namespace(97, 'monomerA', 'monomerB') if mon_b_shift: core.set_global_option("DFT_GRAC_SHIFT", mon_b_shift) core.IO.set_default_namespace('monomerB') wfn_B = scf_helper("SCF", molecule=monomerB, banner="SAPT(DFT): DFT Monomer B", **kwargs) data["DFT MONOMERB"] = core.get_variable("CURRENT ENERGY") core.set_global_option("DFT_GRAC_SHIFT", 0.0) # Print out the title and some information core.print_out("\n") core.print_out( " ---------------------------------------------------------\n") core.print_out(" " + "SAPT(DFT): Intermolecular Interaction Segment".center(58) + "\n") core.print_out("\n") core.print_out(" " + "by Daniel G. A. Smith and Rob Parrish".center(58) + "\n") core.print_out( " ---------------------------------------------------------\n") core.print_out("\n") core.print_out(" ==> Algorithm <==\n\n") core.print_out(" SAPT DFT Functional %12s\n" % str(sapt_dft_functional)) core.print_out(" Monomer A GRAC Shift %12.6f\n" % mon_a_shift) core.print_out(" Monomer B GRAC Shift %12.6f\n" % mon_b_shift) core.print_out(" Delta HF %12s\n" % ("True" if do_delta_hf else "False")) core.print_out(" JK Algorithm %12s\n" % core.get_option("SCF", "SCF_TYPE")) # Build cache and JK sapt_jk = wfn_B.jk() cache = sapt_jk_terms.build_sapt_jk_cache(wfn_A, wfn_B, sapt_jk, True) # Electostatics elst = sapt_jk_terms.electrostatics(cache, True) data.update(elst) # Exchange exch = sapt_jk_terms.exchange(cache, sapt_jk, True) data.update(exch) # Induction ind = sapt_jk_terms.induction(cache, sapt_jk, True, maxiter=core.get_option("SAPT", "MAXITER"), conv=core.get_option("SAPT", "D_CONVERGENCE")) data.update(ind) # Dispersion primary_basis = wfn_A.basisset() core.print_out("\n") aux_basis = core.BasisSet.build(sapt_dimer, "DF_BASIS_MP2", core.get_option("DFMP2", "DF_BASIS_MP2"), "RIFIT", core.get_global_option('BASIS')) fdds_disp = sapt_mp2_terms.df_fdds_dispersion(primary_basis, aux_basis, cache) data.update(fdds_disp) if core.get_option("SAPT", "SAPT_DFT_MP2_DISP_ALG") == "FISAPT": mp2_disp = sapt_mp2_terms.df_mp2_fisapt_dispersion(wfn_A, primary_basis, aux_basis, cache, do_print=True) else: mp2_disp = sapt_mp2_terms.df_mp2_sapt_dispersion(dimer_wfn, wfn_A, wfn_B, primary_basis, aux_basis, cache, do_print=True) data.update(mp2_disp) # Print out final data core.print_out("\n") core.print_out(print_sapt_dft_summary(data, "SAPT(DFT)")) core.tstop() return dimer_wfn
def gradient_forte(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that forte can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> gradient('forte') available for : CASSCF """ # # Start Forte, initialize ambit # my_proc_n_nodes = forte.startup() # my_proc, n_nodes = my_proc_n_nodes # Get the psi4 option object optstash = p4util.OptionsState(['GLOBALS', 'DERTYPE']) psi4.core.set_global_option('DERTYPE', 'FIRST') # Build Forte options options = prepare_forte_options() # Print the banner forte.banner() # Run a method job_type = options.get_str('JOB_TYPE') if job_type not in {"CASSCF", "MCSCF_TWO_STEP"}: raise Exception( 'Analytic energy gradients are only implemented for job_types CASSCF and MCSCF_TWO_STEP.' ) # Prepare Forte objects: state_weights_map, mo_space_info, scf_info forte_objects = prepare_forte_objects(options, name, **kwargs) ref_wfn, state_weights_map, mo_space_info, scf_info, fcidump = forte_objects # Make an integral object time_pre_ints = time.time() ints = forte.make_ints_from_psi4(ref_wfn, options, mo_space_info) start = time.time() # Rotate orbitals before computation orb_type = options.get_str("ORBITAL_TYPE") if orb_type != 'CANONICAL': orb_t = forte.make_orbital_transformation(orb_type, scf_info, options, ints, mo_space_info) orb_t.compute_transformation() Ua = orb_t.get_Ua() Ub = orb_t.get_Ub() ints.rotate_orbitals(Ua, Ub) if job_type == "CASSCF": casscf = forte.make_casscf(state_weights_map, scf_info, options, mo_space_info, ints) energy = casscf.compute_energy() casscf.compute_gradient() if job_type == "MCSCF_TWO_STEP": casscf = forte.make_mcscf_two_step(state_weights_map, scf_info, options, mo_space_info, ints) energy = casscf.compute_energy() time_pre_deriv = time.time() derivobj = psi4.core.Deriv(ref_wfn) derivobj.set_deriv_density_backtransformed(True) derivobj.set_ignore_reference(True) grad = derivobj.compute(psi4.core.DerivCalcType.Correlated) ref_wfn.set_gradient(grad) optstash.restore() end = time.time() # Close ambit, etc. # forte.cleanup() # Print timings psi4.core.print_out('\n\n ==> Forte Timings <==\n') times = [('prepare integrals', start - time_pre_ints), ('run forte energy', time_pre_deriv - start), ('compute derivative integrals', end - time_pre_deriv)] max_key_size = max(len(k) for k, v in times) for key, value in times: psi4.core.print_out(f'\n Time to {key:{max_key_size}} :' f' {value:12.3f} seconds') psi4.core.print_out(f'\n {"Total":{max_key_size + 8}} :' f' {end - time_pre_ints:12.3f} seconds\n') # Dump orbitals if needed if options.get_bool('DUMP_ORBITALS'): dump_orbitals(ref_wfn) return ref_wfn
def run_psi4fockci(name, molecule, **kwargs): """ A method to run a RAS-nSF-IP/EA calculation. This runs a RAS-nSF-IP/EA calculation using Psi4's DETCI module. The number of spin-flips and IP/EA is determined based on setting the ``new_charge`` and ``new_multiplicity`` of the desired target state. Additional excitations are included by setting the ``conf_space`` keyword; excitations through the CISDT level are currently supported. Parameters ---------- name : str Name of method (for Psi4 interfacing) molecule : psi4.core.Molecule Molecule to run the calculation on. new_charge : int Target charge of the molecule. new_multiplicity : int Target multiplicity of the molecule. conf_space : str ("") Option for including additional excitations outside of the CAS space. Defaults to CAS-nSF-IP/EA. Valid options include: * ``""`` CAS-nSF-IP/EA * ``"h"`` RAS(h)-nSF-IP/EA * ``"p"`` RAS(p)-nSF-IP/EA * ``"1x"`` RAS(h,p)-nSF-IP/EA * ``"S"`` RAS(S)-nSF-IP/EA * ``"SD"`` RAS(SD)-nSF-IP/EA * ``"SDT"`` RAS(SDT)-nSF-IP/EA add_opts : dict ({}) Additional options to pass into Psi4. return_ci_wfn : bool (False) Whether to return the CI wavefunction object. return_rohf_wfn : bool (False) Whether to return the ROHF wavefunction object. return_rohf_e : bool (False) Whether to return the ROHF energy. read_rohf_wfn : bool (False) Whether to read a Psi4 ROHF wavefunction. rohf_wfn_in : psi4.core.Wavefunction The Psi4 ROHF reference wavefunction (pre-computed). write_rohf_wfn : str ("") Name of file (.npz) to write localize : bool (False) Perform BOYS localization on the RAS 2 space before DETCI call? Can help with visualization and analysis of orbitals. frozen_docc : int (0) Number of frozen core orbitals. frozen_vir : int (0) Number of frozen virtual orbitals. Returns ------- return_ci_wfn : psi4.core.Wavefunction The SF-CAS([conf_space]) wavefunction. """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) optstash = p4util.OptionsState(['SCF', 'SCF_TYPE'], ['BASIS'], ['SCF', 'MAXITER'], ['DETCI', 'CI_MAXITER']) new_charge = kwargs.get('new_charge') new_multiplicity = kwargs.get('new_multiplicity') ref_mol = molecule if (not 'new_charge' in kwargs): print("ERROR: Please designate a target charge!") exit() if (not 'new_multiplicity' in kwargs): print("ERROR: Please designate a target multiplicity!") exit() conf_space = kwargs.get('conf_space', "") add_opts = kwargs.get('add_opts', {}) read_rohf_wfn = kwargs.get('read_rohf_wfn', False) wfn_rohf_in = kwargs.get('wfn_rohf_in', None) write_rohf_wfn = kwargs.get('write_rohf_wfn', "") write_ci_vects = kwargs.get('write_ci_vects', False) localize = kwargs.get('localize', False) frozen_docc = kwargs.get('frozen_docc', 0) frozen_uocc = kwargs.get('frozen_vir', 0) print("Starting Psi4FockCI...\n") # default options for Psi4 opts = { 'scf_type': 'pk', 'reference': 'rohf', 'mixed': False, 'maxiter': 1000, 'ci_maxiter': 50 } opts.update(add_opts) # add additional options from user # run ROHF calculation on reference state or read it in psi4.core.clean() psi4.set_options(opts) mol = ref_mol.clone() # clone molecule so original isn't modified # read in ROHF guess wavefunction if provided if (read_rohf_wfn): # set up options and run psi4.set_options(opts) print("Using ROHF from reference...") wfn_rohf = wfn_rohf_in e_rohf = wfn_rohf.energy() # else, run ROHF on reference state else: print("Running reference...") # TODO: Change to scf_helper call e_rohf, wfn_rohf = psi4.energy('scf', molecule=mol, return_wfn=True, options=opts) print("SCF (%i %i): %6.12f" % (mol.molecular_charge(), mol.multiplicity(), e_rohf)) # saving npz file of wavefunction if needed if (write_rohf_wfn != ""): wfn_rohf.to_file(write_rohf_wfn) # update molecular charge and multiplicity mol.set_molecular_charge(new_charge) mol.set_multiplicity(new_multiplicity) # set up reference wfn to pass into detci # save orbital values from reference calculation doccpi = wfn_rohf.doccpi()[0] soccpi = wfn_rohf.soccpi()[0] nmo = wfn_rohf.nmo() # calculate soccpi and doccpi new_soccpi = mol.multiplicity() - 1 del_electrons = ref_mol.molecular_charge() - mol.molecular_charge() n_total = wfn_rohf.nalpha() + wfn_rohf.nbeta() + del_electrons # set orbital occupations wfn_rohf.force_soccpi(psi4.core.Dimension([new_soccpi])) wfn_rohf.force_doccpi( psi4.core.Dimension([(int)((n_total - new_soccpi) / 2)])) # set up RAS1, RAS2, RAS3 spaces ras1 = doccpi ras2 = soccpi ras3 = nmo - soccpi - doccpi # add/remove orbitals to active space if ('add_orbs_ras1' in kwargs): ras1 = ras1 - kwargs['add_orbs_ras1'] ras2 = ras2 + kwargs['add_orbs_ras1'] if ('add_orbs_ras3' in kwargs): ras3 = ras3 - kwargs['add_orbs_ras3'] ras2 = ras2 + kwargs['add_orbs_ras3'] # if we need to localize... if (localize): C = psi4.core.Matrix.to_array(wfn_rohf.Ca(), copy=True) ras1_C = C[:, :doccpi] ras2_C = C[:, doccpi:doccpi + soccpi] ras3_C = C[:, doccpi + soccpi:] loc = psi4.core.Localizer.build('BOYS', wfn_rohf.basisset(), psi4.core.Matrix.from_array(ras2_C)) loc.localize() ras2_localized = psi4.core.Matrix.to_array(loc.L, copy=True) localized_orbs = np.column_stack((ras1_C, ras2_localized, ras3_C)) new_Ca = psi4.core.Matrix.from_array(localized_orbs, name="Ca") new_Cb = psi4.core.Matrix.from_array(localized_orbs, name="Cb") wfn_rohf.Ca().copy(new_Ca) wfn_rohf.Cb().copy(new_Cb) # change charge and multiplicity to new target values n_sf = (ref_mol.multiplicity() - abs(del_electrons) - new_multiplicity) / 2 print("\nRunning RAS-SF-IP/EA...") print(" New Charge/Mult: (%i %i)" % (new_charge, new_multiplicity)) print(" Spin-Flips: %i" % n_sf) print(" Electron Count: %i" % del_electrons) # set active space and docc space based on configuration space input # Regular CAS configuration space # includes only active space configurations if (conf_space == "" or conf_space == "CAS"): opts.update({'frozen_docc': [doccpi]}) opts.update({'ras1': [0]}) opts.update({'ras2': [ras2]}) opts.update({'ras3': [0]}) opts.update({'ras4': [0]}) # just (h) excitations elif (conf_space == "h"): opts.update({'ex_level': 0}) opts.update({'val_ex_level': 1}) opts.update({'ras3_max': 0}) opts.update({'frozen_docc': [frozen_docc]}) opts.update({'ras1': [ras1 - frozen_docc]}) opts.update({'ras2': [ras2]}) opts.update({'ras3': [0]}) opts.update({'ras4': [0]}) # just (p) excitations elif (conf_space == "p"): opts.update({'ex_level': 0}) opts.update({'val_ex_level': 0}) opts.update({'ras3_max': 1}) opts.update({'frozen_docc': [doccpi]}) opts.update({'ras1': [0]}) opts.update({'ras2': [ras2]}) opts.update({'ras3': [ras3 - frozen_uocc]}) opts.update({'frozen_uocc': [frozen_uocc]}) opts.update({'ras4': [0]}) # 1x configuration space # includes (h, p) excitations elif (conf_space == "1x" or conf_space == "h,p"): opts.update({'frozen_docc': [0]}) opts.update({'ex_level': 0}) opts.update({'val_ex_level': 1}) opts.update({'ras3_max': 1}) opts.update({'frozen_docc': [frozen_docc]}) opts.update({'ras1': [ras1 - frozen_docc]}) opts.update({'ras2': [ras2]}) opts.update({'ras3': [ras3 - frozen_uocc]}) opts.update({'frozen_uocc': [frozen_uocc]}) opts.update({'ras4': [0]}) # S configuration space # includes (h, p, hp) excitations elif (conf_space == "s"): opts.update({'ex_level': 1}) opts.update({'frozen_docc': [frozen_docc]}) opts.update({'ras1': [ras1 - frozen_docc]}) opts.update({'ras2': [ras2]}) opts.update({'ras3': [ras3 - frozen_uocc]}) opts.update({'frozen_uocc': [frozen_uocc]}) opts.update({'ras4': [0]}) elif (conf_space == "sd"): opts.update({'frozen_docc': [0]}) opts.update({'ex_level': 2}) opts.update({'frozen_docc': [frozen_docc]}) opts.update({'ras1': [ras1 - frozen_docc]}) opts.update({'ras2': [ras2]}) opts.update({'ras3': [ras3 - frozen_uocc]}) opts.update({'frozen_uocc': [frozen_uocc]}) opts.update({'ras4': [0]}) elif (conf_space == "sdt"): opts.update({'frozen_docc': [0]}) opts.update({'ex_level': 3}) opts.update({'frozen_docc': [frozen_docc]}) opts.update({'ras1': [ras1 - frozen_docc]}) opts.update({'ras2': [ras2]}) opts.update({'ras3': [ras3 - frozen_uocc]}) opts.update({'frozen_uocc': [frozen_uocc]}) opts.update({'ras4': [0]}) # Other configuration spaces aren't supported yet else: print("Configuration space %s not supported. Exiting..." % conf_space) exit() # run cas psi4.set_options(opts) e_cas, wfn_cas = psi4.energy('detci', ref_wfn=wfn_rohf, return_wfn=True, molecule=mol) # printing useful info print("\n Root\tEnergy") print("-----------------------------------") n = 0 while (psi4.core.has_variable("CI ROOT %i TOTAL ENERGY" % n)): n_str = "CI ROOT %i TOTAL ENERGY" % n e_n = psi4.core.variable(n_str) print(" %4i\t%6.12f" % (n, e_n)) n = n + 1 print("-----------------------------------\n") psi4.core.print_variables() # printing Psi4 variables # obtain eigenvectors if needed # partly based on Daniel Smith's answer on Psi4 forums if (write_ci_vects): wfn_cas_2 = psi4.core.CIWavefunction(wfn_rohf) C = np.zeros((wfn_cas_2.ndet(), n_roots)) for i in range(n_roots): dvec = wfn_cas_2.new_civector(i + 1, 53, True, True) dvec.set_nvec(i + 1) dvec.init_io_files(True) dvec.read(i, 0) C[:, i] = np.array(dvec) np.savetxt('ci_vect.txt', C) optstash.restore() print("Psi4FockCI complete. Have a good day!") return wfn_cas