def run_sapt(): if ddlow.name() == 'SCF': dimer_wfn = ddlow else: dimer_wfn = ddlow.reference_wavefunction() aux_basis = core.BasisSet.build(dimer_wfn.molecule(), "DF_BASIS_SAPT", core.get_global_option("DF_BASIS_SAPT"), "RIFIT", core.get_global_option("BASIS")) dimer_wfn.set_basisset("DF_BASIS_SAPT", aux_basis) dimer_wfn.set_basisset("DF_BASIS_ELST", aux_basis) core.sapt(dimer_wfn, m1mlow, m2mlow) return {k: core.get_variable(k) for k in ('SAPT ELST10,R ENERGY', 'SAPT EXCH10 ENERGY', 'SAPT EXCH10(S^2) ENERGY', 'SAPT IND20,R ENERGY', 'SAPT EXCH-IND20,R ENERGY', 'SAPT EXCH-DISP20 ENERGY', 'SAPT DISP20 ENERGY', 'SAPT SAME-SPIN EXCH-DISP20 ENERGY', 'SAPT SAME-SPIN DISP20 ENERGY', 'SAPT HF TOTAL ENERGY', )}
def psi4_dryrun(wfn,my_options,cache,comp_hash,psi_variable=None): """ This function mock runs a Psi4 call as if Psi4 obeyed the Pulsar framework API fully. More specifically, it checks the cache for an answer, if it finds it, it then returns it, otherwise it skims the Psi variables. See [Using the Cache with Psi4](@ref cacheing) for more details. NOTE: We don't want, for example, CCSD to have to know that it contains MP2 and HF because MP2 knows it contains HF. Instead, we only want CCSD to deal with the fact that it contains MP2. Furthermore, Psi4 needs all options set at the begining so each module has to do this through the only call it has, i.e. the deriv function, that function ultimately calls this function, so this function also has to set the options. This isn't ideal, but the alternative is having every method know all submethods it contains, which I like less... wfn (psr.datastore.wavefunction) : the wavefunction to cache my_options (psr.datastore.OptionMap) : the options, in Pulsar format cache (psr.datastore.CacheData) : the cache for this module comp_hash (string) : the hash of this computation psi_variable (string) : the Psi variable Psi4 uses for this energy quantity NOTE: Only energies work. To my knowledge Psi4 doesn't support getting say the HF component of the MP2 gradient. """ out=psr.get_global_output() data=cache.get(comp_hash,True) if data: return data out.debug("Did not use cached value for: "+psi_variable+"\n") if psi_variable and psi4.has_variable(psi_variable): Egy=psi4.get_variable(psi_variable) data=(wfn,[Egy]) cache.set(comp_hash,data,CheckpointPolicy) return cache.get(comp_hash,True) return wfn,[]
def print_ci_results(ciwfn, rname, scf_e, ci_e, print_opdm_no=False): """ Printing for all CI Wavefunctions """ # Print out energetics core.print_out("\n ==> Energetics <==\n\n") core.print_out(" SCF energy = %20.15f\n" % scf_e) if "CI" in rname: core.print_out(" Total CI energy = %20.15f\n" % ci_e) elif "MP" in rname: core.print_out(" Total MP energy = %20.15f\n" % ci_e) elif "ZAPT" in rname: core.print_out(" Total ZAPT energy = %20.15f\n" % ci_e) else: core.print_out(" Total MCSCF energy = %20.15f\n" % ci_e) # Nothing to be done for ZAPT or MP if ("MP" in rname) or ("ZAPT" in rname): core.print_out("\n") return # Initial info ci_nroots = core.get_option("DETCI", "NUM_ROOTS") irrep_labels = ciwfn.molecule().irrep_labels() # Grab the D-vector dvec = ciwfn.D_vector() dvec.init_io_files(True) for root in range(ci_nroots): core.print_out("\n ==> %s root %d information <==\n\n" % (rname, root)) # Print total energy root_e = core.get_variable("CI ROOT %d TOTAL ENERGY" % (root)) core.print_out(" %s Root %d energy = %20.15f\n" % (rname, root, root_e)) # Print natural occupations if print_opdm_no: core.print_out("\n Active Space Natural occupation numbers:\n\n") occs_list = [] r_opdm = ciwfn.get_opdm(root, root, "SUM", False) for h in range(len(r_opdm.nph)): if 0 in r_opdm.nph[h].shape: continue nocc, rot = np.linalg.eigh(r_opdm.nph[h]) for e in nocc: occs_list.append((e, irrep_labels[h])) occs_list.sort(key=lambda x: -x[0]) cnt = 0 for value, label in occs_list: value, label = occs_list[cnt] core.print_out(" %4s % 8.6f" % (label, value)) cnt += 1 if (cnt % 3) == 0: core.print_out("\n") if (cnt % 3): core.print_out("\n") # Print CIVector information ciwfn.print_vector(dvec, root) # True to keep the file dvec.close_io_files(True)
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(" !!! WARNING: SAPT(DFT) capability is in beta. Please use with caution. !!!\n\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 run_sapt_dft(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") # 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(" !!! WARNING: SAPT(DFT) capability is in beta. Please use with caution. !!!\n\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_global_option("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_global_option('SCF_TYPE') == 'DF'): # core.set_global_option('DF_INTS_IO', 'LOAD') core.set_global_option('DF_INTS_IO', 'SAVE') # # Compute dimer wavefunction hf_wfn_dimer = None if do_delta_hf: if (core.get_global_option('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_global_option('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_global_option('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) # Grab JK object and set to A (so we do not save many JK objects) sapt_jk = hf_wfn_B.jk() hf_wfn_A.set_jk(sapt_jk) core.set_global_option("SAVE_JK", False) # Move it back to monomer A if (core.get_global_option('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 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() del hf_wfn_A, hf_wfn_B, sapt_jk 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_global_option('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_global_option('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") # Save JK object sapt_jk = wfn_B.jk() wfn_A.set_jk(sapt_jk) core.set_global_option("SAVE_JK", False) core.set_global_option("DFT_GRAC_SHIFT", 0.0) # Write out header scf_alg = core.get_global_option("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 nbody_gufunc(func, method_string, **kwargs): """ Computes the nbody interaction energy, gradient, or Hessian depending on input. This is a generalized univeral function for computing interaction quantities. :returns: *return type of func* |w--w| The interaction data. :returns: (*float*, :py:class:`~psi4.core.Wavefunction`) |w--w| interaction data and wavefunction with energy/gradient/hessian set appropriately when **return_wfn** specified. :type func: function :param func: ``energy`` || etc. Python function that accepts method_string and a molecule. Returns a energy, gradient, or Hessian as requested. :type method_string: string :param method_string: ``'scf'`` || ``'mp2'`` || ``'ci5'`` || etc. First argument, lowercase and usually unlabeled. Indicates the computational method to be passed to func. :type molecule: :ref:`molecule <op_py_molecule>` :param molecule: ``h2o`` || etc. The target molecule, if not the last molecule defined. :type return_wfn: :ref:`boolean <op_py_boolean>` :param return_wfn: ``'on'`` || |dl| ``'off'`` |dr| Indicate to additionally return the :py:class:`~psi4.core.Wavefunction` calculation result as the second element of a tuple. :type bsse_type: string or list :param bsse_type: ``'cp'`` || ``['nocp', 'vmfc']`` || |dl| ``None`` |dr| || etc. Type of BSSE correction to compute: CP, NoCP, or VMFC. The first in this list is returned by this function. By default, this function is not called. :type max_nbody: int :param max_nbody: ``3`` || etc. Maximum n-body to compute, cannot exceed the number of fragments in the moleucle. :type ptype: string :param ptype: ``'energy'`` || ``'gradient'`` || ``'hessian'`` Type of the procedure passed in. :type return_total_data: :ref:`boolean <op_py_boolean>` :param return_total_data: ``'on'`` || |dl| ``'off'`` |dr| If True returns the total data (energy/gradient/etc) of the system, otherwise returns interaction data. """ ### ==> Parse some kwargs <== kwargs = p4util.kwargs_lower(kwargs) return_wfn = kwargs.pop('return_wfn', False) ptype = kwargs.pop('ptype', None) return_total_data = kwargs.pop('return_total_data', False) molecule = kwargs.pop('molecule', core.get_active_molecule()) molecule.update_geometry() core.clean_variables() if ptype not in ['energy', 'gradient', 'hessian']: raise ValidationError("""N-Body driver: The ptype '%s' is not regonized.""" % ptype) # Figure out BSSE types do_cp = False do_nocp = False do_vmfc = False return_method = False # Must be passed bsse_type bsse_type_list = kwargs.pop('bsse_type') if bsse_type_list is None: raise ValidationError("N-Body GUFunc: Must pass a bsse_type") if not isinstance(bsse_type_list, list): bsse_type_list = [bsse_type_list] for num, btype in enumerate(bsse_type_list): if btype.lower() == 'cp': do_cp = True if (num == 0): return_method = 'cp' elif btype.lower() == 'nocp': do_nocp = True if (num == 0): return_method = 'nocp' elif btype.lower() == 'vmfc': do_vmfc = True if (num == 0): return_method = 'vmfc' else: raise ValidationError("N-Body GUFunc: bsse_type '%s' is not recognized" % btype.lower()) max_nbody = kwargs.get('max_nbody', -1) max_frag = molecule.nfragments() if max_nbody == -1: max_nbody = molecule.nfragments() else: max_nbody = min(max_nbody, max_frag) # What levels do we need? nbody_range = range(1, max_nbody + 1) fragment_range = range(1, max_frag + 1) # Flip this off for now, needs more testing # If we are doing CP lets save them integrals #if 'cp' in bsse_type_list and (len(bsse_type_list) == 1): # # Set to save RI integrals for repeated full-basis computations # ri_ints_io = core.get_global_option('DF_INTS_IO') # # inquire if above at all applies to dfmp2 or just scf # core.set_global_option('DF_INTS_IO', 'SAVE') # psioh = core.IOManager.shared_object() # psioh.set_specific_retention(97, True) bsse_str = bsse_type_list[0] if len(bsse_type_list) >1: bsse_str = str(bsse_type_list) core.print_out("\n\n") core.print_out(" ===> N-Body Interaction Abacus <===\n") core.print_out(" BSSE Treatment: %s\n" % bsse_str) cp_compute_list = {x:set() for x in nbody_range} nocp_compute_list = {x:set() for x in nbody_range} vmfc_compute_list = {x:set() for x in nbody_range} vmfc_level_list = {x:set() for x in nbody_range} # Need to sum something slightly different # Build up compute sets if do_cp: # Everything is in dimer basis basis_tuple = tuple(fragment_range) for nbody in nbody_range: for x in it.combinations(fragment_range, nbody): cp_compute_list[nbody].add( (x, basis_tuple) ) if do_nocp: # Everything in monomer basis for nbody in nbody_range: for x in it.combinations(fragment_range, nbody): nocp_compute_list[nbody].add( (x, x) ) if do_vmfc: # Like a CP for all combinations of pairs or greater for nbody in nbody_range: for cp_combos in it.combinations(fragment_range, nbody): basis_tuple = tuple(cp_combos) for interior_nbody in nbody_range: for x in it.combinations(cp_combos, interior_nbody): combo_tuple = (x, basis_tuple) vmfc_compute_list[interior_nbody].add( combo_tuple ) vmfc_level_list[len(basis_tuple)].add( combo_tuple ) # Build a comprehensive compute_range compute_list = {x:set() for x in nbody_range} for n in nbody_range: compute_list[n] |= cp_compute_list[n] compute_list[n] |= nocp_compute_list[n] compute_list[n] |= vmfc_compute_list[n] core.print_out(" Number of %d-body computations: %d\n" % (n, len(compute_list[n]))) # Build size and slices dictionaries fragment_size_dict = {frag: molecule.extract_subsets(frag).natom() for frag in range(1, max_frag+1)} start = 0 fragment_slice_dict = {} for k, v in fragment_size_dict.items(): fragment_slice_dict[k] = slice(start, start + v) start += v molecule_total_atoms = sum(fragment_size_dict.values()) # Now compute the energies energies_dict = {} ptype_dict = {} for n in compute_list.keys(): core.print_out("\n ==> N-Body: Now computing %d-body complexes <==\n\n" % n) total = len(compute_list[n]) for num, pair in enumerate(compute_list[n]): core.print_out("\n N-Body: Computing complex (%d/%d) with fragments %s in the basis of fragments %s.\n\n" % (num + 1, total, str(pair[0]), str(pair[1]))) ghost = list(set(pair[1]) - set(pair[0])) current_mol = molecule.extract_subsets(list(pair[0]), ghost) ptype_dict[pair] = func(method_string, molecule=current_mol, **kwargs) energies_dict[pair] = core.get_variable("CURRENT ENERGY") core.print_out("\n N-Body: Complex Energy (fragments = %s, basis = %s: %20.14f)\n" % (str(pair[0]), str(pair[1]), energies_dict[pair])) # Flip this off for now, needs more testing #if 'cp' in bsse_type_list and (len(bsse_type_list) == 1): # core.set_global_option('DF_INTS_IO', 'LOAD') core.clean() # Final dictionaries cp_energy_by_level = {n: 0.0 for n in nbody_range} nocp_energy_by_level = {n: 0.0 for n in nbody_range} cp_energy_body_dict = {n: 0.0 for n in nbody_range} nocp_energy_body_dict = {n: 0.0 for n in nbody_range} vmfc_energy_body_dict = {n: 0.0 for n in nbody_range} # Build out ptype dictionaries if needed if ptype != 'energy': if ptype == 'gradient': arr_shape = (molecule_total_atoms, 3) elif ptype == 'hessian': arr_shape = (molecule_total_atoms * 3, molecule_total_atoms * 3) else: raise KeyError("N-Body: ptype '%s' not recognized" % ptype) cp_ptype_by_level = {n: np.zeros(arr_shape) for n in nbody_range} nocp_ptype_by_level = {n: np.zeros(arr_shape) for n in nbody_range} vmfc_ptype_by_level = {n: np.zeros(arr_shape) for n in nbody_range} cp_ptype_body_dict = {n: np.zeros(arr_shape) for n in nbody_range} nocp_ptype_body_dict = {n: np.zeros(arr_shape) for n in nbody_range} vmfc_ptype_body_dict = {n: np.zeros(arr_shape) for n in nbody_range} else: cp_ptype_by_level, cp_ptype_body_dict = None, None nocp_ptype_by_level, nocp_ptype_body_dict = None, None vmfc_ptype_body_dict = None # Sum up all of the levels for n in nbody_range: # Energy cp_energy_by_level[n] = sum(energies_dict[v] for v in cp_compute_list[n]) nocp_energy_by_level[n] = sum(energies_dict[v] for v in nocp_compute_list[n]) # Special vmfc case if n > 1: vmfc_energy_body_dict[n] = vmfc_energy_body_dict[n - 1] for tup in vmfc_level_list[n]: vmfc_energy_body_dict[n] += ((-1) ** (n - len(tup[0]))) * energies_dict[tup] # Do ptype if ptype != 'energy': _sum_cluster_ptype_data(ptype, ptype_dict, cp_compute_list[n], fragment_slice_dict, fragment_size_dict, cp_ptype_by_level[n]) _sum_cluster_ptype_data(ptype, ptype_dict, nocp_compute_list[n], fragment_slice_dict, fragment_size_dict, nocp_ptype_by_level[n]) _sum_cluster_ptype_data(ptype, ptype_dict, vmfc_level_list[n], fragment_slice_dict, fragment_size_dict, vmfc_ptype_by_level[n], vmfc=True) # Compute cp energy and ptype if do_cp: for n in nbody_range: if n == max_frag: cp_energy_body_dict[n] = cp_energy_by_level[n] if ptype != 'energy': cp_ptype_body_dict[n][:] = cp_ptype_by_level[n] continue for k in range(1, n + 1): take_nk = nCr(max_frag - k - 1, n - k) sign = ((-1) ** (n - k)) value = cp_energy_by_level[k] cp_energy_body_dict[n] += take_nk * sign * value if ptype != 'energy': value = cp_ptype_by_level[k] cp_ptype_body_dict[n] += take_nk * sign * value _print_nbody_energy(cp_energy_body_dict, "Counterpoise Corrected (CP)") cp_interaction_energy = cp_energy_body_dict[max_nbody] - cp_energy_body_dict[1] core.set_variable('Counterpoise Corrected Total Energy', cp_energy_body_dict[max_nbody]) core.set_variable('Counterpoise Corrected Interaction Energy', cp_interaction_energy) for n in nbody_range[1:]: var_key = 'CP-CORRECTED %d-BODY INTERACTION ENERGY' % n core.set_variable(var_key, cp_energy_body_dict[n] - cp_energy_body_dict[1]) # Compute nocp energy and ptype if do_nocp: for n in nbody_range: if n == max_frag: nocp_energy_body_dict[n] = nocp_energy_by_level[n] if ptype != 'energy': nocp_ptype_body_dict[n][:] = nocp_ptype_by_level[n] continue for k in range(1, n + 1): take_nk = nCr(max_frag - k - 1, n - k) sign = ((-1) ** (n - k)) value = nocp_energy_by_level[k] nocp_energy_body_dict[n] += take_nk * sign * value if ptype != 'energy': value = nocp_ptype_by_level[k] nocp_ptype_body_dict[n] += take_nk * sign * value _print_nbody_energy(nocp_energy_body_dict, "Non-Counterpoise Corrected (NoCP)") nocp_interaction_energy = nocp_energy_body_dict[max_nbody] - nocp_energy_body_dict[1] core.set_variable('Non-Counterpoise Corrected Total Energy', nocp_energy_body_dict[max_nbody]) core.set_variable('Non-Counterpoise Corrected Interaction Energy', nocp_interaction_energy) for n in nbody_range[1:]: var_key = 'NOCP-CORRECTED %d-BODY INTERACTION ENERGY' % n core.set_variable(var_key, nocp_energy_body_dict[n] - nocp_energy_body_dict[1]) # Compute vmfc energy and ptype if do_vmfc: _print_nbody_energy(vmfc_energy_body_dict, "Valiron-Mayer Function Couterpoise (VMFC)") vmfc_interaction_energy = vmfc_energy_body_dict[max_nbody] - vmfc_energy_body_dict[1] core.set_variable('Valiron-Mayer Function Couterpoise Total Energy', vmfc_energy_body_dict[max_nbody]) core.set_variable('Valiron-Mayer Function Couterpoise Interaction Energy', vmfc_interaction_energy) for n in nbody_range[1:]: var_key = 'VMFC-CORRECTED %d-BODY INTERACTION ENERGY' % n core.set_variable(var_key, vmfc_energy_body_dict[n] - vmfc_energy_body_dict[1]) if return_method == 'cp': ptype_body_dict = cp_ptype_body_dict energy_body_dict = cp_energy_body_dict elif return_method == 'nocp': ptype_body_dict = nocp_ptype_body_dict energy_body_dict = nocp_energy_body_dict elif return_method == 'vmfc': ptype_body_dict = vmfc_ptype_body_dict energy_body_dict = vmfc_energy_body_dict else: raise ValidationError("N-Body Wrapper: Invalid return type. Should never be here, please post this error on github.") # Figure out and build return types if return_total_data: ret_energy = energy_body_dict[max_nbody] else: ret_energy = energy_body_dict[max_nbody] ret_energy -= energy_body_dict[1] if ptype != 'energy': if return_total_data: np_final_ptype = ptype_body_dict[max_nbody].copy() else: np_final_ptype = ptype_body_dict[max_nbody].copy() np_final_ptype -= ptype_body_dict[1] ret_ptype = core.Matrix.from_array(np_final_ptype) else: ret_ptype = ret_energy # Build and set a wavefunction wfn = core.Wavefunction.build(molecule, 'sto-3g') wfn.nbody_energy = energies_dict wfn.nbody_ptype = ptype_dict wfn.nbody_body_energy = energy_body_dict wfn.nbody_body_ptype = ptype_body_dict if ptype == 'gradient': wfn.set_gradient(ret_ptype) elif ptype == 'hessian': wfn.set_hessian(ret_ptype) core.set_variable("CURRENT ENERGY", ret_energy) if return_wfn: return (ret_ptype, wfn) else: return ret_ptype
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'], ['FREEZE_CORE'], ['MP2_TYPE'], ['SCF','SCF_TYPE']) # override default scf_type core.set_local_option('SCF','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.get_variable('INTERNAL ENERGY CORRECTION') dh = core.get_variable('ENTHALPY CORRECTION') dg = core.get_variable('GIBBS FREE ENERGY CORRECTION') freqs = ref.frequencies() nfreq = freqs.dim(0) freqsum = 0.0 for i in range(0, nfreq): freqsum += freqs.get(i) zpe = freqsum / 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.get_variable("QCISD(T) TOTAL ENERGY") emp4_6311gd = core.get_variable("MP4 TOTAL ENERGY") emp2_6311gd = core.get_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.get_variable("MP4 TOTAL ENERGY") emp2_6311pg_dp = core.get_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.get_variable("MP4 TOTAL ENERGY") emp2_6311g2dfp = core.get_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.get_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 mcscf_solver(ref_wfn): # Build CIWavefunction core.prepare_options_for_module("DETCI") ciwfn = core.CIWavefunction(ref_wfn) # Hush a lot of CI output ciwfn.set_print(0) # Begin with a normal two-step step_type = 'Initial CI' total_step = core.Matrix("Total step", ciwfn.get_dimension('OA'), ciwfn.get_dimension('AV')) start_orbs = ciwfn.get_orbitals("ROT").clone() ciwfn.set_orbitals("ROT", start_orbs) # Grab da options mcscf_orb_grad_conv = core.get_option("DETCI", "MCSCF_R_CONVERGENCE") mcscf_e_conv = core.get_option("DETCI", "MCSCF_E_CONVERGENCE") mcscf_max_macroiteration = core.get_option("DETCI", "MCSCF_MAXITER") mcscf_type = core.get_option("DETCI", "MCSCF_TYPE") mcscf_d_file = core.get_option("DETCI", "CI_FILE_START") + 3 mcscf_nroots = core.get_option("DETCI", "NUM_ROOTS") mcscf_wavefunction_type = core.get_option("DETCI", "WFN") mcscf_ndet = ciwfn.ndet() mcscf_nuclear_energy = ciwfn.molecule().nuclear_repulsion_energy() mcscf_steplimit = core.get_option("DETCI", "MCSCF_MAX_ROT") mcscf_rotate = core.get_option("DETCI", "MCSCF_ROTATE") # DIIS info mcscf_diis_start = core.get_option("DETCI", "MCSCF_DIIS_START") mcscf_diis_freq = core.get_option("DETCI", "MCSCF_DIIS_FREQ") mcscf_diis_error_type = core.get_option("DETCI", "MCSCF_DIIS_ERROR_TYPE") mcscf_diis_max_vecs = core.get_option("DETCI", "MCSCF_DIIS_MAX_VECS") # One-step info mcscf_target_conv_type = core.get_option("DETCI", "MCSCF_ALGORITHM") mcscf_so_start_grad = core.get_option("DETCI", "MCSCF_SO_START_GRAD") mcscf_so_start_e = core.get_option("DETCI", "MCSCF_SO_START_E") mcscf_current_step_type = 'Initial CI' # Start with SCF energy and other params scf_energy = core.get_variable("HF TOTAL ENERGY") eold = scf_energy norb_iter = 1 converged = False ah_step = False qc_step = False approx_integrals_only = True # Fake info to start with the inital diagonalization ediff = 1.e-4 orb_grad_rms = 1.e-3 # Grab needed objects diis_obj = solvers.DIIS(mcscf_diis_max_vecs) mcscf_obj = ciwfn.mcscf_object() # Execute the rotate command for rot in mcscf_rotate: if len(rot) != 4: raise p4util.PsiException("Each element of the MCSCF rotate command requires 4 arguements (irrep, orb1, orb2, theta).") irrep, orb1, orb2, theta = rot if irrep > ciwfn.Ca().nirrep(): raise p4util.PsiException("MCSCF_ROTATE: Expression %s irrep number is larger than the number of irreps" % (str(rot))) if max(orb1, orb2) > ciwfn.Ca().coldim()[irrep]: raise p4util.PsiException("MCSCF_ROTATE: Expression %s orbital number exceeds number of orbitals in irrep" % (str(rot))) theta = np.deg2rad(theta) x = ciwfn.Ca().nph[irrep][:, orb1].copy() y = ciwfn.Ca().nph[irrep][:, orb2].copy() xp = np.cos(theta) * x - np.sin(theta) * y yp = np.sin(theta) * x + np.cos(theta) * y ciwfn.Ca().nph[irrep][:, orb1] = xp ciwfn.Ca().nph[irrep][:, orb2] = yp # Limited RAS functionality if core.get_local_option("DETCI", "WFN") == "RASSCF" and mcscf_target_conv_type != "TS": core.print_out("\n Warning! Only the TS algorithm for RASSCF wavefunction is currently supported.\n") core.print_out(" Switching to the TS algorithm.\n\n") mcscf_target_conv_type = "TS" # Print out headers if mcscf_type == "CONV": mtype = " @MCSCF" core.print_out("\n ==> Starting MCSCF iterations <==\n\n") core.print_out(" Iter Total Energy Delta E Orb RMS CI RMS NCI NORB\n") elif mcscf_type == "DF": mtype = " @DF-MCSCF" core.print_out("\n ==> Starting DF-MCSCF iterations <==\n\n") core.print_out(" Iter Total Energy Delta E Orb RMS CI RMS NCI NORB\n") else: mtype = " @AO-MCSCF" core.print_out("\n ==> Starting AO-MCSCF iterations <==\n\n") core.print_out(" Iter Total Energy Delta E Orb RMS CI RMS NCI NORB\n") # Iterate ! for mcscf_iter in range(1, mcscf_max_macroiteration + 1): # Transform integrals, diagonalize H ciwfn.transform_mcscf_integrals(approx_integrals_only) nci_iter = ciwfn.diag_h(abs(ediff) * 1.e-2, orb_grad_rms * 1.e-3) # After the first diag we need to switch to READ ciwfn.set_ci_guess("DFILE") ciwfn.form_opdm() ciwfn.form_tpdm() ci_grad_rms = core.get_variable("DETCI AVG DVEC NORM") # Update MCSCF object Cocc = ciwfn.get_orbitals("DOCC") Cact = ciwfn.get_orbitals("ACT") Cvir = ciwfn.get_orbitals("VIR") opdm = ciwfn.get_opdm(-1, -1, "SUM", False) tpdm = ciwfn.get_tpdm("SUM", True) mcscf_obj.update(Cocc, Cact, Cvir, opdm, tpdm) current_energy = core.get_variable("MCSCF TOTAL ENERGY") orb_grad_rms = mcscf_obj.gradient_rms() ediff = current_energy - eold # Print iterations print_iteration(mtype, mcscf_iter, current_energy, ediff, orb_grad_rms, ci_grad_rms, nci_iter, norb_iter, mcscf_current_step_type) eold = current_energy if mcscf_current_step_type == 'Initial CI': mcscf_current_step_type = 'TS' # Check convergence if (orb_grad_rms < mcscf_orb_grad_conv) and (abs(ediff) < abs(mcscf_e_conv)) and\ (mcscf_iter > 3) and not qc_step: core.print_out("\n %s has converged!\n\n" % mtype); converged = True break # Which orbital convergence are we doing? if ah_step: converged, norb_iter, step = ah_iteration(mcscf_obj, print_micro=False) norb_iter += 1 if converged: mcscf_current_step_type = 'AH' else: core.print_out(" !Warning. Augmented Hessian did not converge. Taking an approx step.\n") step = mcscf_obj.approx_solve() mcscf_current_step_type = 'TS, AH failure' else: step = mcscf_obj.approx_solve() step_type = 'TS' maxstep = step.absmax() if maxstep > mcscf_steplimit: core.print_out(' Warning! Maxstep = %4.2f, scaling to %4.2f\n' % (maxstep, mcscf_steplimit)) step.scale(mcscf_steplimit / maxstep) xstep = total_step.clone() total_step.add(step) # Do or add DIIS if (mcscf_iter >= mcscf_diis_start) and ("TS" in mcscf_current_step_type): # Figure out DIIS error vector if mcscf_diis_error_type == "GRAD": error = core.Matrix.triplet(ciwfn.get_orbitals("OA"), mcscf_obj.gradient(), ciwfn.get_orbitals("AV"), False, False, True) else: error = step diis_obj.add(total_step, error) if not (mcscf_iter % mcscf_diis_freq): total_step = diis_obj.extrapolate() mcscf_current_step_type = 'TS, DIIS' # Build the rotation by continuous updates if mcscf_iter == 1: totalU = mcscf_obj.form_rotation_matrix(total_step) else: xstep.axpy(-1.0, total_step) xstep.scale(-1.0) Ustep = mcscf_obj.form_rotation_matrix(xstep) totalU = core.Matrix.doublet(totalU, Ustep, False, False) # Build the rotation directly (not recommended) # orbs_mat = mcscf_obj.Ck(start_orbs, total_step) # Finally rotate and set orbitals orbs_mat = core.Matrix.doublet(start_orbs, totalU, False, False) ciwfn.set_orbitals("ROT", orbs_mat) # Figure out what the next step should be if (orb_grad_rms < mcscf_so_start_grad) and (abs(ediff) < abs(mcscf_so_start_e)) and\ (mcscf_iter >= 2): if mcscf_target_conv_type == 'AH': approx_integrals_only = False ah_step = True elif mcscf_target_conv_type == 'OS': approx_integrals_only = False mcscf_current_step_type = 'OS, Prep' break else: continue #raise p4util.PsiException("") # If we converged do not do onestep if converged or (mcscf_target_conv_type != 'OS'): one_step_iters = [] # If we are not converged load in Dvec and build iters array else: one_step_iters = range(mcscf_iter + 1, mcscf_max_macroiteration + 1) dvec = ciwfn.D_vector() dvec.init_io_files(True) dvec.read(0, 0) dvec.symnormalize(1.0, 0) ci_grad = ciwfn.new_civector(1, mcscf_d_file + 1, True, True) ci_grad.set_nvec(1) ci_grad.init_io_files(True) # Loop for onestep for mcscf_iter in one_step_iters: # Transform integrals and update the MCSCF object ciwfn.transform_mcscf_integrals(ciwfn.H(), False) ciwfn.form_opdm() ciwfn.form_tpdm() # Update MCSCF object Cocc = ciwfn.get_orbitals("DOCC") Cact = ciwfn.get_orbitals("ACT") Cvir = ciwfn.get_orbitals("VIR") opdm = ciwfn.get_opdm(-1, -1, "SUM", False) tpdm = ciwfn.get_tpdm("SUM", True) mcscf_obj.update(Cocc, Cact, Cvir, opdm, tpdm) orb_grad_rms = mcscf_obj.gradient_rms() # Warning! Does not work for SA-MCSCF current_energy = mcscf_obj.current_total_energy() current_energy += mcscf_nuclear_energy core.set_variable("CI ROOT %d TOTAL ENERGY" % 1, current_energy) core.set_variable("CURRENT ENERGY", current_energy) docc_energy = mcscf_obj.current_docc_energy() ci_energy = mcscf_obj.current_ci_energy() # Compute CI gradient ciwfn.sigma(dvec, ci_grad, 0, 0) ci_grad.scale(2.0, 0) ci_grad.axpy(-2.0 * ci_energy, dvec, 0, 0) ci_grad_rms = ci_grad.norm(0) orb_grad_rms = mcscf_obj.gradient().rms() ediff = current_energy - eold print_iteration(mtype, mcscf_iter, current_energy, ediff, orb_grad_rms, ci_grad_rms, nci_iter, norb_iter, mcscf_current_step_type) mcscf_current_step_type = 'OS' eold = current_energy if (orb_grad_rms < mcscf_orb_grad_conv) and (abs(ediff) < abs(mcscf_e_conv)): core.print_out("\n %s has converged!\n\n" % mtype); converged = True break # Take a step converged, norb_iter, nci_iter, step = qc_iteration(dvec, ci_grad, ciwfn, mcscf_obj) # Rotate integrals to new frame total_step.add(step) orbs_mat = mcscf_obj.Ck(ciwfn.get_orbitals("ROT"), step) ciwfn.set_orbitals("ROT", orbs_mat) core.print_out(mtype + " Final Energy: %20.15f\n" % current_energy) # Die if we did not converge if (not converged): if core.get_global_option("DIE_IF_NOT_CONVERGED"): raise p4util.PsiException("MCSCF: Iterations did not converge!") else: core.print_out("\nWarning! MCSCF iterations did not converge!\n\n") # Print out CI vector information if mcscf_target_conv_type == 'OS': dvec.close_io_files() ci_grad.close_io_files() # For orbital invariant methods we transform the orbitals to the natural or # semicanonical basis. Frozen doubly occupied and virtual orbitals are not # modified. if core.get_option("DETCI", "WFN") == "CASSCF": # Do we diagonalize the opdm? if core.get_option("DETCI", "NAT_ORBS"): ciwfn.ci_nat_orbs() else: ciwfn.semicanonical_orbs() # Retransform intragrals and update CI coeffs., OPDM, and TPDM ciwfn.transform_mcscf_integrals(approx_integrals_only) nci_iter = ciwfn.diag_h(abs(ediff) * 1.e-2, orb_grad_rms * 1.e-3) ciwfn.set_ci_guess("DFILE") ciwfn.form_opdm() ciwfn.form_tpdm() proc_util.print_ci_results(ciwfn, "MCSCF", scf_energy, current_energy, print_opdm_no=True) # Set final energy core.set_variable("CURRENT ENERGY", core.get_variable("MCSCF TOTAL ENERGY")) # What do we need to cleanup? if core.get_option("DETCI", "MCSCF_CI_CLEANUP"): ciwfn.cleanup_ci() if core.get_option("DETCI", "MCSCF_DPD_CLEANUP"): ciwfn.cleanup_dpd() del diis_obj del mcscf_obj return ciwfn
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.get_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 compute_nbody_components(func, method_string, metadata): """Computes requested N-body components. Performs requested computations for psi4::Molecule object `molecule` according to `compute_list` with function `func` at `method_string` level of theory. Parameters ---------- func : {'energy', 'gradient', 'hessian'} Function object to be called within N-Body procedure. method_string : str Indicates level of theory to be passed to function `func`. metadata : dict of str Dictionary of N-body metadata. Required ``'key': value`` pairs: ``'compute_list'``: dict of int: set List of computations to perform. Keys indicate body-levels, e.g,. `compute_list[2]` is the list of all 2-body computations required. ``'kwargs'``: dict Arbitrary keyword arguments to be passed to function `func`. Returns ------- dict of str: dict Dictionary containing computed N-body components. Contents: ``'energies'``: dict of set: float64 Dictionary containing all energy components required for given N-body procedure. ``'ptype'``: dict of set: float64 or dict of set: psi4.Matrix Dictionary of returned quantities from calls of function `func` during N-body computations ``'intermediates'``: dict of str: float64 Dictionary of psivars for intermediate N-body computations to be set at the end of the N-body procedure. """ # Get required metadata kwargs = metadata['kwargs'] molecule = metadata['molecule'] #molecule = core.get_active_molecule() compute_list = metadata['compute_dict']['all'] # Now compute the energies energies_dict = {} ptype_dict = {} intermediates_dict = {} for n in compute_list.keys(): core.print_out("\n ==> N-Body: Now computing %d-body complexes <==\n\n" % n) total = len(compute_list[n]) for num, pair in enumerate(compute_list[n]): core.print_out("\n N-Body: Computing complex (%d/%d) with fragments %s in the basis of fragments %s.\n\n" % (num + 1, total, str(pair[0]), str(pair[1]))) ghost = list(set(pair[1]) - set(pair[0])) current_mol = molecule.extract_subsets(list(pair[0]), ghost) # Save energies info ptype_dict[pair] = func(method_string, molecule=current_mol, **kwargs) energies_dict[pair] = core.get_variable("CURRENT ENERGY") var_key = "N-BODY (%s)@(%s) TOTAL ENERGY" % (', '.join([str(i) for i in pair[0]]), ', '.join([str(i) for i in pair[1]])) intermediates_dict[var_key] = core.get_variable("CURRENT ENERGY") core.print_out("\n N-Body: Complex Energy (fragments = %s, basis = %s: %20.14f)\n" % (str(pair[0]), str(pair[1]), energies_dict[pair])) # Flip this off for now, needs more testing #if 'cp' in bsse_type_list and (len(bsse_type_list) == 1): # core.set_global_option('DF_INTS_IO', 'LOAD') core.clean() return {'energies': energies_dict, 'ptype': ptype_dict, 'intermediates': intermediates_dict}
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.get_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.get_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 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.get_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 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 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() 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 = {} 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)")) # Copy data back into globals for k, v in data.items(): core.set_variable(k, v) core.tstop() return dimer_wfn
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'], ['FREEZE_CORE'], ['MP2_TYPE'], ['SCF','SCF_TYPE']) # override default scf_type core.set_local_option('SCF','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.get_variable('INTERNAL ENERGY CORRECTION') dh = core.get_variable('ENTHALPY CORRECTION') dg = core.get_variable('GIBBS FREE ENERGY CORRECTION') freqs = ref.frequencies() nfreq = freqs.dim(0) freqsum = 0.0 for i in range(0, nfreq): freqsum += freqs.get(i) zpe = freqsum / p4const.psi_hartree2wavenumbers * 0.8929 * 0.5 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.get_variable("QCISD(T) TOTAL ENERGY") emp4_6311gd = core.get_variable("MP4 TOTAL ENERGY") emp2_6311gd = core.get_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.get_variable("MP4 TOTAL ENERGY") emp2_6311pg_dp = core.get_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.get_variable("MP4 TOTAL ENERGY") emp2_6311g2dfp = core.get_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.get_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 nbody_gufunc(func, method_string, **kwargs): """ Computes the nbody interaction energy, gradient, or Hessian depending on input. This is a generalized univeral function for computing interaction quantities. :returns: *return type of func* |w--w| The interaction data. :returns: (*float*, :ref:`Wavefunction<sec:psimod_Wavefunction>`) |w--w| interaction data and wavefunction with energy/gradient/hessian set appropriately when **return_wfn** specified. :type func: function :param func: ``energy`` || etc. Python function that accepts method_string and a molecule. Returns a energy, gradient, or Hessian as requested. :type method_string: string :param method_string: ``'scf'`` || ``'mp2'`` || ``'ci5'`` || etc. First argument, lowercase and usually unlabeled. Indicates the computational method to be passed to func. :type molecule: :ref:`molecule <op_py_molecule>` :param molecule: ``h2o`` || etc. The target molecule, if not the last molecule defined. :type return_wfn: :ref:`boolean <op_py_boolean>` :param return_wfn: ``'on'`` || |dl| ``'off'`` |dr| Indicate to additionally return the :ref:`Wavefunction<sec:psimod_Wavefunction>` calculation result as the second element of a tuple. :type bsse_type: string or list :param bsse_type: ``'cp'`` || ``['nocp', 'vmfc']`` || |dl| ``None`` |dr| || etc. Type of BSSE correction to compute: CP, NoCP, or VMFC. The first in this list is returned by this function. By default, this function is not called. :type max_nbody: int :param max_nbody: ``3`` || etc. Maximum n-body to compute, cannot exceed the number of fragments in the moleucle. :type ptype: string :param ptype: ``'energy'`` || ``'gradient'`` || ``'hessian'`` Type of the procedure passed in. :type return_total_data: :ref:`boolean <op_py_boolean>` :param return_total_data: ``'on'`` || |dl| ``'off'`` |dr| If True returns the total data (energy/gradient/etc) of the system, otherwise returns interaction data. """ ### ==> Parse some kwargs <== kwargs = p4util.kwargs_lower(kwargs) return_wfn = kwargs.pop('return_wfn', False) ptype = kwargs.pop('ptype', None) return_total_data = kwargs.pop('return_total_data', False) molecule = kwargs.pop('molecule', core.get_active_molecule()) molecule.update_geometry() core.clean_variables() if ptype not in ['energy', 'gradient', 'hessian']: raise ValidationError( """N-Body driver: The ptype '%s' is not regonized.""" % ptype) # Figure out BSSE types do_cp = False do_nocp = False do_vmfc = False return_method = False # Must be passed bsse_type bsse_type_list = kwargs.pop('bsse_type') if bsse_type_list is None: raise ValidationError("N-Body GUFunc: Must pass a bsse_type") if not isinstance(bsse_type_list, list): bsse_type_list = [bsse_type_list] for num, btype in enumerate(bsse_type_list): if btype.lower() == 'cp': do_cp = True if (num == 0): return_method = 'cp' elif btype.lower() == 'nocp': do_nocp = True if (num == 0): return_method = 'nocp' elif btype.lower() == 'vmfc': do_vmfc = True if (num == 0): return_method = 'vmfc' else: raise ValidationError( "N-Body GUFunc: bsse_type '%s' is not recognized" % btype.lower()) max_nbody = kwargs.get('max_nbody', -1) max_frag = molecule.nfragments() if max_nbody == -1: max_nbody = molecule.nfragments() else: max_nbody = min(max_nbody, max_frag) # What levels do we need? nbody_range = range(1, max_nbody + 1) fragment_range = range(1, max_frag + 1) # Flip this off for now, needs more testing # If we are doing CP lets save them integrals #if 'cp' in bsse_type_list and (len(bsse_type_list) == 1): # # Set to save RI integrals for repeated full-basis computations # ri_ints_io = core.get_global_option('DF_INTS_IO') # # inquire if above at all applies to dfmp2 or just scf # core.set_global_option('DF_INTS_IO', 'SAVE') # psioh = core.IOManager.shared_object() # psioh.set_specific_retention(97, True) bsse_str = bsse_type_list[0] if len(bsse_type_list) > 1: bsse_str = str(bsse_type_list) core.print_out("\n\n") core.print_out(" ===> N-Body Interaction Abacus <===\n") core.print_out(" BSSE Treatment: %s\n" % bsse_str) cp_compute_list = {x: set() for x in nbody_range} nocp_compute_list = {x: set() for x in nbody_range} vmfc_compute_list = {x: set() for x in nbody_range} vmfc_level_list = {x: set() for x in nbody_range } # Need to sum something slightly different # Build up compute sets if do_cp: # Everything is in dimer basis basis_tuple = tuple(fragment_range) for nbody in nbody_range: for x in it.combinations(fragment_range, nbody): cp_compute_list[nbody].add((x, basis_tuple)) if do_nocp: # Everything in monomer basis for nbody in nbody_range: for x in it.combinations(fragment_range, nbody): nocp_compute_list[nbody].add((x, x)) if do_vmfc: # Like a CP for all combinations of pairs or greater for nbody in nbody_range: for cp_combos in it.combinations(fragment_range, nbody): basis_tuple = tuple(cp_combos) for interior_nbody in nbody_range: for x in it.combinations(cp_combos, interior_nbody): combo_tuple = (x, basis_tuple) vmfc_compute_list[interior_nbody].add(combo_tuple) vmfc_level_list[len(basis_tuple)].add(combo_tuple) # Build a comprehensive compute_range compute_list = {x: set() for x in nbody_range} for n in nbody_range: compute_list[n] |= cp_compute_list[n] compute_list[n] |= nocp_compute_list[n] compute_list[n] |= vmfc_compute_list[n] core.print_out(" Number of %d-body computations: %d\n" % (n, len(compute_list[n]))) # Build size and slices dictionaries fragment_size_dict = { frag: molecule.extract_subsets(frag).natom() for frag in range(1, max_frag + 1) } start = 0 fragment_slice_dict = {} for k, v in fragment_size_dict.items(): fragment_slice_dict[k] = slice(start, start + v) start += v molecule_total_atoms = sum(fragment_size_dict.values()) # Now compute the energies energies_dict = {} ptype_dict = {} for n in compute_list.keys(): core.print_out( "\n ==> N-Body: Now computing %d-body complexes <==\n\n" % n) print("\n ==> N-Body: Now computing %d-body complexes <==\n" % n) total = len(compute_list[n]) for num, pair in enumerate(compute_list[n]): core.print_out( "\n N-Body: Computing complex (%d/%d) with fragments %s in the basis of fragments %s.\n\n" % (num + 1, total, str(pair[0]), str(pair[1]))) ghost = list(set(pair[1]) - set(pair[0])) current_mol = molecule.extract_subsets(list(pair[0]), ghost) ptype_dict[pair] = func(method_string, molecule=current_mol, **kwargs) energies_dict[pair] = core.get_variable("CURRENT ENERGY") core.print_out( "\n N-Body: Complex Energy (fragments = %s, basis = %s: %20.14f)\n" % (str(pair[0]), str(pair[1]), energies_dict[pair])) # Flip this off for now, needs more testing #if 'cp' in bsse_type_list and (len(bsse_type_list) == 1): # core.set_global_option('DF_INTS_IO', 'LOAD') core.clean() # Final dictionaries cp_energy_by_level = {n: 0.0 for n in nbody_range} nocp_energy_by_level = {n: 0.0 for n in nbody_range} cp_energy_body_dict = {n: 0.0 for n in nbody_range} nocp_energy_body_dict = {n: 0.0 for n in nbody_range} vmfc_energy_body_dict = {n: 0.0 for n in nbody_range} # Build out ptype dictionaries if needed if ptype != 'energy': if ptype == 'gradient': arr_shape = (molecule_total_atoms, 3) elif ptype == 'hessian': arr_shape = (molecule_total_atoms * 3, molecule_total_atoms * 3) else: raise KeyError("N-Body: ptype '%s' not recognized" % ptype) cp_ptype_by_level = {n: np.zeros(arr_shape) for n in nbody_range} nocp_ptype_by_level = {n: np.zeros(arr_shape) for n in nbody_range} vmfc_ptype_by_level = {n: np.zeros(arr_shape) for n in nbody_range} cp_ptype_body_dict = {n: np.zeros(arr_shape) for n in nbody_range} nocp_ptype_body_dict = {n: np.zeros(arr_shape) for n in nbody_range} vmfc_ptype_body_dict = {n: np.zeros(arr_shape) for n in nbody_range} else: cp_ptype_by_level, cp_ptype_body_dict = None, None nocp_ptype_by_level, nocp_ptype_body_dict = None, None vmfc_ptype_body_dict = None # Sum up all of the levels for n in nbody_range: # Energy cp_energy_by_level[n] = sum(energies_dict[v] for v in cp_compute_list[n]) nocp_energy_by_level[n] = sum(energies_dict[v] for v in nocp_compute_list[n]) # Special vmfc case if n > 1: vmfc_energy_body_dict[n] = vmfc_energy_body_dict[n - 1] for tup in vmfc_level_list[n]: vmfc_energy_body_dict[n] += ( (-1)**(n - len(tup[0]))) * energies_dict[tup] # Do ptype if ptype != 'energy': _sum_cluster_ptype_data(ptype, ptype_dict, cp_compute_list[n], fragment_slice_dict, fragment_size_dict, cp_ptype_by_level[n]) _sum_cluster_ptype_data(ptype, ptype_dict, nocp_compute_list[n], fragment_slice_dict, fragment_size_dict, nocp_ptype_by_level[n]) _sum_cluster_ptype_data(ptype, ptype_dict, vmfc_level_list[n], fragment_slice_dict, fragment_size_dict, vmfc_ptype_by_level[n], vmfc=True) # Compute cp energy and ptype if do_cp: for n in nbody_range: if n == max_frag: cp_energy_body_dict[n] = cp_energy_by_level[n] if ptype != 'energy': cp_ptype_body_dict[n][:] = cp_ptype_by_level[n] continue for k in range(1, n + 1): take_nk = nCr(max_frag - k - 1, n - k) sign = ((-1)**(n - k)) value = cp_energy_by_level[k] cp_energy_body_dict[n] += take_nk * sign * value if ptype != 'energy': value = cp_ptype_by_level[k] cp_ptype_body_dict[n] += take_nk * sign * value _print_nbody_energy(cp_energy_body_dict, "Counterpoise Corrected (CP)") cp_interaction_energy = cp_energy_body_dict[ max_nbody] - cp_energy_body_dict[1] core.set_variable('Counterpoise Corrected Total Energy', cp_energy_body_dict[max_nbody]) core.set_variable('Counterpoise Corrected Interaction Energy', cp_interaction_energy) for n in nbody_range[1:]: var_key = 'CP-CORRECTED %d-BODY INTERACTION ENERGY' % n core.set_variable(var_key, cp_energy_body_dict[n] - cp_energy_body_dict[1]) # Compute nocp energy and ptype if do_nocp: for n in nbody_range: if n == max_frag: nocp_energy_body_dict[n] = nocp_energy_by_level[n] if ptype != 'energy': nocp_ptype_body_dict[n][:] = nocp_ptype_by_level[n] continue for k in range(1, n + 1): take_nk = nCr(max_frag - k - 1, n - k) sign = ((-1)**(n - k)) value = nocp_energy_by_level[k] nocp_energy_body_dict[n] += take_nk * sign * value if ptype != 'energy': value = nocp_ptype_by_level[k] nocp_ptype_body_dict[n] += take_nk * sign * value _print_nbody_energy(nocp_energy_body_dict, "Non-Counterpoise Corrected (NoCP)") nocp_interaction_energy = nocp_energy_body_dict[ max_nbody] - nocp_energy_body_dict[1] core.set_variable('Non-Counterpoise Corrected Total Energy', nocp_energy_body_dict[max_nbody]) core.set_variable('Non-Counterpoise Corrected Interaction Energy', nocp_interaction_energy) for n in nbody_range[1:]: var_key = 'NOCP-CORRECTED %d-BODY INTERACTION ENERGY' % n core.set_variable( var_key, nocp_energy_body_dict[n] - nocp_energy_body_dict[1]) # Compute vmfc energy and ptype if do_vmfc: _print_nbody_energy(vmfc_energy_body_dict, "Valiron-Mayer Function Couterpoise (VMFC)") vmfc_interaction_energy = vmfc_energy_body_dict[ max_nbody] - vmfc_energy_body_dict[1] core.set_variable('Valiron-Mayer Function Couterpoise Total Energy', vmfc_energy_body_dict[max_nbody]) core.set_variable( 'Valiron-Mayer Function Couterpoise Interaction Energy', vmfc_interaction_energy) for n in nbody_range[1:]: var_key = 'VMFC-CORRECTED %d-BODY INTERACTION ENERGY' % n core.set_variable( var_key, vmfc_energy_body_dict[n] - vmfc_energy_body_dict[1]) if return_method == 'cp': ptype_body_dict = cp_ptype_body_dict energy_body_dict = cp_energy_body_dict elif return_method == 'nocp': ptype_body_dict = nocp_ptype_body_dict energy_body_dict = nocp_energy_body_dict elif return_method == 'vmfc': ptype_body_dict = vmfc_ptype_body_dict energy_body_dict = vmfc_energy_body_dict else: raise ValidationError( "N-Body Wrapper: Invalid return type. Should never be here, please post this error on github." ) # Figure out and build return types if return_total_data: ret_energy = energy_body_dict[max_nbody] else: ret_energy = energy_body_dict[max_nbody] ret_energy -= energy_body_dict[1] if ptype != 'energy': if return_total_data: np_final_ptype = ptype_body_dict[max_nbody].copy() else: np_final_ptype = ptype_body_dict[max_nbody].copy() np_final_ptype -= ptype_body_dict[1] ret_ptype = core.Matrix.from_array(np_final_ptype) else: ret_ptype = ret_energy # Build and set a wavefunction wfn = core.Wavefunction.build(molecule, 'sto-3g') wfn.cdict["nbody_energy"] = energies_dict wfn.cdict["nbody_ptype"] = ptype_dict wfn.cdict["nbody_body_energy"] = energy_body_dict wfn.cdict["nbody_body_ptype"] = ptype_body_dict if ptype == 'gradient': wfn.set_gradient(ret_ptype) elif ptype == 'hessian': wfn.set_hessian(ret_ptype) core.set_variable("CURRENT ENERGY", ret_energy) if return_wfn: return (ret_ptype, wfn) else: return ret_ptype
def frac_traverse(name, **kwargs): """Scan electron occupancy from +1 electron to -1. Parameters ---------- name : string, functional 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.get_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.get_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 mcscf_solver(ref_wfn): # Build CIWavefunction core.prepare_options_for_module("DETCI") ciwfn = core.CIWavefunction(ref_wfn) # Hush a lot of CI output ciwfn.set_print(0) # Begin with a normal two-step step_type = 'Initial CI' total_step = core.Matrix("Total step", ciwfn.get_dimension('OA'), ciwfn.get_dimension('AV')) start_orbs = ciwfn.get_orbitals("ROT").clone() ciwfn.set_orbitals("ROT", start_orbs) # Grab da options mcscf_orb_grad_conv = core.get_option("DETCI", "MCSCF_R_CONVERGENCE") mcscf_e_conv = core.get_option("DETCI", "MCSCF_E_CONVERGENCE") mcscf_max_macroiteration = core.get_option("DETCI", "MCSCF_MAXITER") mcscf_type = core.get_option("DETCI", "MCSCF_TYPE") mcscf_d_file = core.get_option("DETCI", "CI_FILE_START") + 3 mcscf_nroots = core.get_option("DETCI", "NUM_ROOTS") mcscf_wavefunction_type = core.get_option("DETCI", "WFN") mcscf_ndet = ciwfn.ndet() mcscf_nuclear_energy = ciwfn.molecule().nuclear_repulsion_energy() mcscf_steplimit = core.get_option("DETCI", "MCSCF_MAX_ROT") mcscf_rotate = core.get_option("DETCI", "MCSCF_ROTATE") # DIIS info mcscf_diis_start = core.get_option("DETCI", "MCSCF_DIIS_START") mcscf_diis_freq = core.get_option("DETCI", "MCSCF_DIIS_FREQ") mcscf_diis_error_type = core.get_option("DETCI", "MCSCF_DIIS_ERROR_TYPE") mcscf_diis_max_vecs = core.get_option("DETCI", "MCSCF_DIIS_MAX_VECS") # One-step info mcscf_target_conv_type = core.get_option("DETCI", "MCSCF_ALGORITHM") mcscf_so_start_grad = core.get_option("DETCI", "MCSCF_SO_START_GRAD") mcscf_so_start_e = core.get_option("DETCI", "MCSCF_SO_START_E") mcscf_current_step_type = 'Initial CI' # Start with SCF energy and other params scf_energy = core.get_variable("HF TOTAL ENERGY") eold = scf_energy norb_iter = 1 converged = False ah_step = False qc_step = False # Fake info to start with the inital diagonalization ediff = 1.e-4 orb_grad_rms = 1.e-3 # Grab needed objects diis_obj = diis_helper.DIIS_helper(mcscf_diis_max_vecs) mcscf_obj = ciwfn.mcscf_object() # Execute the rotate command for rot in mcscf_rotate: if len(rot) != 4: raise p4util.PsiException( "Each element of the MCSCF rotate command requires 4 arguements (irrep, orb1, orb2, theta)." ) irrep, orb1, orb2, theta = rot if irrep > ciwfn.Ca().nirrep(): raise p4util.PsiException( "MCSCF_ROTATE: Expression %s irrep number is larger than the number of irreps" % (str(rot))) if max(orb1, orb2) > ciwfn.Ca().coldim()[irrep]: raise p4util.PsiException( "MCSCF_ROTATE: Expression %s orbital number exceeds number of orbitals in irrep" % (str(rot))) theta = np.deg2rad(theta) x = ciwfn.Ca().nph[irrep][:, orb1].copy() y = ciwfn.Ca().nph[irrep][:, orb2].copy() xp = np.cos(theta) * x - np.sin(theta) * y yp = np.sin(theta) * x + np.cos(theta) * y ciwfn.Ca().nph[irrep][:, orb1] = xp ciwfn.Ca().nph[irrep][:, orb2] = yp # Limited RAS functionality if core.get_local_option( "DETCI", "WFN") == "RASSCF" and mcscf_target_conv_type != "TS": core.print_out( "\n Warning! Only the TS algorithm for RASSCF wavefunction is currently supported.\n" ) core.print_out(" Switching to the TS algorithm.\n\n") mcscf_target_conv_type = "TS" # Print out headers if mcscf_type == "CONV": mtype = " @MCSCF" core.print_out("\n ==> Starting MCSCF iterations <==\n\n") core.print_out( " Iter Total Energy Delta E Orb RMS CI RMS NCI NORB\n" ) else: mtype = " @DF-MCSCF" core.print_out("\n ==> Starting DF-MCSCF iterations <==\n\n") core.print_out( " Iter Total Energy Delta E Orb RMS CI RMS NCI NORB\n" ) # Iterate ! for mcscf_iter in range(1, mcscf_max_macroiteration + 1): # Transform integrals, diagonalize H ciwfn.transform_mcscf_integrals(mcscf_current_step_type == 'TS') nci_iter = ciwfn.diag_h(abs(ediff) * 1.e-2, orb_grad_rms * 1.e-3) ciwfn.form_opdm() ciwfn.form_tpdm() ci_grad_rms = core.get_variable("DETCI AVG DVEC NORM") # Update MCSCF object Cocc = ciwfn.get_orbitals("DOCC") Cact = ciwfn.get_orbitals("ACT") Cvir = ciwfn.get_orbitals("VIR") opdm = ciwfn.get_opdm(-1, -1, "SUM", False) tpdm = ciwfn.get_tpdm("SUM", True) mcscf_obj.update(Cocc, Cact, Cvir, opdm, tpdm) current_energy = core.get_variable('CURRENT ENERGY') orb_grad_rms = mcscf_obj.gradient_rms() ediff = current_energy - eold # Print iterations print_iteration(mtype, mcscf_iter, current_energy, ediff, orb_grad_rms, ci_grad_rms, nci_iter, norb_iter, mcscf_current_step_type) eold = current_energy if mcscf_current_step_type == 'Initial CI': mcscf_current_step_type = 'TS' # Check convergence if (orb_grad_rms < mcscf_orb_grad_conv) and (abs(ediff) < abs(mcscf_e_conv)) and\ (mcscf_iter > 3) and not qc_step: core.print_out("\n %s has converged!\n\n" % mtype) converged = True break # Which orbital convergence are we doing? if ah_step: converged, norb_iter, step = ah_iteration(mcscf_obj, print_micro=False) norb_iter += 1 if converged: mcscf_current_step_type = 'AH' else: core.print_out( " !Warning. Augmented Hessian did not converge. Taking an approx step.\n" ) step = mcscf_obj.approx_solve() mcscf_current_step_type = 'TS, AH failure' else: step = mcscf_obj.approx_solve() step_type = 'TS' maxstep = step.absmax() if maxstep > mcscf_steplimit: core.print_out( ' Warning! Maxstep = %4.2f, scaling to %4.2f\n' % (maxstep, mcscf_steplimit)) step.scale(mcscf_steplimit / maxstep) total_step.add(step) # Do or add DIIS if (mcscf_iter >= mcscf_diis_start) and ("TS" in mcscf_current_step_type): # Figure out DIIS error vector if mcscf_diis_error_type == "GRAD": error = core.Matrix.triplet(ciwfn.get_orbitals("OA"), mcscf_obj.gradient(), ciwfn.get_orbitals("AV"), False, False, True) else: error = step diis_obj.add(total_step, error) if not (mcscf_iter % mcscf_diis_freq): total_step = diis_obj.extrapolate() mcscf_current_step_type = 'TS, DIIS' # Finally rotate and set orbitals orbs_mat = mcscf_obj.Ck(start_orbs, total_step) ciwfn.set_orbitals("ROT", orbs_mat) # Figure out what the next step should be if (orb_grad_rms < mcscf_so_start_grad) and (abs(ediff) < abs(mcscf_so_start_e)) and\ (mcscf_iter >= 2): if mcscf_target_conv_type == 'AH': ah_step = True elif mcscf_target_conv_type == 'OS': mcscf_current_step_type = 'OS, Prep' break else: continue #raise p4util.PsiException("") # If we converged do not do onestep if converged or (mcscf_target_conv_type != 'OS'): one_step_iters = [] # If we are not converged load in Dvec and build iters array else: one_step_iters = range(mcscf_iter + 1, mcscf_max_macroiteration + 1) dvec = ciwfn.new_civector(1, mcscf_d_file, True, True) dvec.set_nvec(1) dvec.init_io_files(True) dvec.read(0, 0) dvec.symnormalize(1.0, 0) ci_grad = ciwfn.new_civector(1, mcscf_d_file + 1, True, True) ci_grad.set_nvec(1) ci_grad.init_io_files(True) # Loop for onestep for mcscf_iter in one_step_iters: # Transform integrals and update the MCSCF object ciwfn.transform_mcscf_integrals(False) ciwfn.form_opdm() ciwfn.form_tpdm() # Update MCSCF object Cocc = ciwfn.get_orbitals("DOCC") Cact = ciwfn.get_orbitals("ACT") Cvir = ciwfn.get_orbitals("VIR") opdm = ciwfn.get_opdm(-1, -1, "SUM", False) tpdm = ciwfn.get_tpdm("SUM", True) mcscf_obj.update(Cocc, Cact, Cvir, opdm, tpdm) orb_grad_rms = mcscf_obj.gradient_rms() # Warning! Does not work for SA-MCSCF current_energy = mcscf_obj.current_total_energy() current_energy += mcscf_nuclear_energy core.set_variable("CI ROOT %d TOTAL ENERGY" % 1, current_energy) core.set_variable("CURRENT ENERGY", current_energy) docc_energy = mcscf_obj.current_docc_energy() ci_energy = mcscf_obj.current_ci_energy() # Compute CI gradient ciwfn.sigma(dvec, ci_grad, 0, 0) ci_grad.scale(2.0, 0) ci_grad.axpy(-2.0 * ci_energy, dvec, 0, 0) ci_grad_rms = ci_grad.norm(0) orb_grad_rms = mcscf_obj.gradient().rms() ediff = current_energy - eold print_iteration(mtype, mcscf_iter, current_energy, ediff, orb_grad_rms, ci_grad_rms, nci_iter, norb_iter, mcscf_current_step_type) mcscf_current_step_type = 'OS' eold = current_energy if (orb_grad_rms < mcscf_orb_grad_conv) and (abs(ediff) < abs(mcscf_e_conv)): core.print_out("\n %s has converged!\n\n" % mtype) converged = True break # Take a step converged, norb_iter, nci_iter, step = qc_iteration( dvec, ci_grad, ciwfn, mcscf_obj) # Rotate integrals to new frame total_step.add(step) orbs_mat = mcscf_obj.Ck(ciwfn.get_orbitals("ROT"), step) ciwfn.set_orbitals("ROT", orbs_mat) core.print_out(mtype + " Final Energy: %20.15f\n" % current_energy) # Die if we did not converge if (not converged): if core.get_global_option("DIE_IF_NOT_CONVERGED"): raise p4util.PsiException("MCSCF: Iterations did not converge!") else: core.print_out("\nWarning! MCSCF iterations did not converge!\n\n") # Print out energetics core.print_out("\n ==> Energetics <==\n\n") core.print_out(" SCF energy = %20.15f\n" % scf_energy) core.print_out(" Total CI energy = %20.15f\n\n" % current_energy) # Print out CI vector information if mcscf_target_conv_type != 'SO': dvec = ciwfn.new_civector(mcscf_nroots, mcscf_d_file, True, True) dvec.init_io_files(True) irrep_labels = ciwfn.molecule().irrep_labels() for root in range(mcscf_nroots): core.print_out("\n ==> CI root %d information <==\n\n" % (root + 1)) # Print total energy root_e = core.get_variable("CI ROOT %d TOTAL ENERGY" % (root + 1)) core.print_out(" CI Root %2d energy = %20.15f\n" % (root + 1, root_e)) # Print natural occupations core.print_out("\n Natural occupation numbers:\n\n") occs_list = [] r_opdm = ciwfn.get_opdm(root, root, "SUM", False) for h in range(len(r_opdm.nph)): if 0 in r_opdm.nph[h].shape: continue nocc, rot = np.linalg.eigh(r_opdm.nph[h]) for e in nocc: occs_list.append((e, irrep_labels[h])) occs_list.sort(key=lambda x: -x[0]) cnt = 0 for value, label in occs_list: value, label = occs_list[cnt] core.print_out(" %4s % 8.6f" % (label, value)) cnt += 1 if (cnt % 3) == 0: core.print_out("\n") # Print CIVector information ciwfn.print_vector(dvec, root) # What do we need to cleanup? if core.get_option("DETCI", "MCSCF_CI_CLEANUP"): ciwfn.cleanup_ci() if core.get_option("DETCI", "MCSCF_DPD_CLEANUP"): # print('Cleaning up DPD data!') ciwfn.cleanup_dpd() del diis_obj del mcscf_obj return ciwfn