def scf_initialize(self): """Specialized initialization, compute integrals and does everything to prepare for iterations""" self.iteration_ = 0 efp_enabled = hasattr(self.molecule(), 'EFP') if core.get_option('SCF', "PRINT") > 0: core.print_out(" ==> Pre-Iterations <==\n\n") self.print_preiterations() if efp_enabled: # EFP: Set QM system, options, and callback. Display efp geom in [A] efpobj = self.molecule().EFP core.print_out(efpobj.banner()) core.print_out(efpobj.geometry_summary(units_to_bohr=constants.bohr2angstroms)) efpptc, efpcoords, efpopts = get_qm_atoms_opts(self.molecule()) efpobj.set_point_charges(efpptc, efpcoords) efpobj.set_opts(efpopts, label='psi', append='psi') efpobj.set_electron_density_field_fn(field_fn) if self.attempt_number_ == 1: mints = core.MintsHelper(self.basisset()) if core.get_global_option('RELATIVISTIC') in ['X2C', 'DKH']: mints.set_rel_basisset(self.get_basisset('BASIS_RELATIVISTIC')) mints.one_electron_integrals() self.integrals() core.timer_on("HF: Form core H") self.form_H() core.timer_off("HF: Form core H") if efp_enabled: # EFP: Add in permanent moment contribution and cache core.timer_on("HF: Form Vefp") verbose = core.get_option('SCF', "PRINT") Vefp = modify_Fock_permanent(self.molecule(), mints, verbose=verbose-1) Vefp = core.Matrix.from_array(Vefp) self.H().add(Vefp) Horig = self.H().clone() self.Horig = Horig core.print_out(" QM/EFP: iterating Total Energy including QM/EFP Induction\n") core.timer_off("HF: Form Vefp") core.timer_on("HF: Form S/X") self.form_Shalf() core.timer_off("HF: Form S/X") core.timer_on("HF: Guess") self.guess() core.timer_off("HF: Guess") else: # We're reading the orbitals from the previous set of iterations. self.form_D() self.set_energies("Total Energy", self.compute_initial_E())
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') core.timer_on("SAPT(DFT): Dimer SCF") hf_data = {} hf_wfn_dimer = scf_helper("SCF", molecule=sapt_dimer, banner="SAPT(DFT): delta HF Dimer", **kwargs) hf_data["HF DIMER"] = core.variable("CURRENT ENERGY") core.timer_off("SAPT(DFT): Dimer SCF") core.timer_on("SAPT(DFT): Monomer A SCF") 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.variable("CURRENT ENERGY") core.timer_off("SAPT(DFT): Monomer A SCF") core.timer_on("SAPT(DFT): Monomer B SCF") 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.variable("CURRENT ENERGY") core.set_global_option("SAVE_JK", False) core.timer_off("SAPT(DFT): Monomer B SCF") # 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 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") # Build cache hf_cache = sapt_jk_terms.build_sapt_jk_cache(hf_wfn_A, hf_wfn_B, sapt_jk, True) # Electrostatics core.timer_on("SAPT(DFT):SAPT:elst") elst = sapt_jk_terms.electrostatics(hf_cache, True) hf_data.update(elst) core.timer_off("SAPT(DFT):SAPT:elst") # Exchange core.timer_on("SAPT(DFT):SAPT:exch") exch = sapt_jk_terms.exchange(hf_cache, sapt_jk, True) hf_data.update(exch) core.timer_off("SAPT(DFT):SAPT:exch") # Induction core.timer_on("SAPT(DFT):SAPT:ind") 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) core.timer_off("SAPT(DFT):SAPT: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.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 core.timer_on("SAPT(DFT): Monomer A DFT") 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) 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.variable("CURRENT ENERGY") core.set_global_option("DFT_GRAC_SHIFT", 0.0) core.timer_off("SAPT(DFT): Monomer A DFT") # Compute Monomer B wavefunction core.timer_on("SAPT(DFT): Monomer B DFT") 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.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) core.timer_off("SAPT(DFT): Monomer B DFT") # 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 sapt_dft(dimer_wfn, wfn_A, wfn_B, sapt_jk=None, sapt_jk_B=None, data=None, print_header=True, cleanup_jk=True): """ The primary SAPT(DFT) algorithm to compute the interaction energy once the wavefunctions have been built. Example ------- dimer = psi4.geometry(''' Ne -- Ar 1 6.5 units bohr ''') psi4.set_options({"BASIS": "aug-cc-pVDZ"}) # Prepare the fragments sapt_dimer, monomerA, monomerB = psi4.proc_util.prepare_sapt_molecule(sapt_dimer, "dimer") # Run the first monomer set DFT_GRAC_SHIFT 0.203293 wfnA, energyA = psi4.energy("PBE0", monomer=monomerA, return_wfn=True) # Run the second monomer set DFT_GRAC_SHIFT 0.138264 wfnB, energyB = psi4.energy("PBE0", monomer=monomerB, return_wfn=True) # Build the dimer wavefunction wfnD = psi4.core.Wavefunction.build(sapt_dimer) # Compute SAPT(DFT) from the provided wavefunctions data = psi4.procrouting.sapt.sapt_dft(wfnD, wfnA, wfnB) """ # Handle the input options core.timer_on("SAPT(DFT):SAPT(DFT):JK") if print_header: sapt_dft_header() if sapt_jk is None: core.print_out("\n => Building SAPT JK object <= \n\n") sapt_jk = core.JK.build(dimer_wfn.basisset()) sapt_jk.set_do_J(True) sapt_jk.set_do_K(True) if wfn_A.functional().is_x_lrc(): sapt_jk.set_do_wK(True) sapt_jk.set_omega(wfn_A.functional().x_omega()) sapt_jk.initialize() sapt_jk.print_header() if wfn_B.functional().is_x_lrc() and (wfn_A.functional().x_omega() != wfn_B.functional().x_omega()): core.print_out(" => Monomer B: Building SAPT JK object <= \n\n") core.print_out( " Reason: MonomerA Omega != MonomerB Omega\n\n") sapt_jk_B = core.JK.build(dimer_wfn.basisset()) sapt_jk_B.set_do_J(True) sapt_jk_B.set_do_K(True) sapt_jk_B.set_do_wK(True) sapt_jk_B.set_omega(wfn_B.functional().x_omega()) sapt_jk_B.initialize() sapt_jk_B.print_header() else: sapt_jk.set_do_K(True) if data is None: data = {} # Build SAPT cache cache = sapt_jk_terms.build_sapt_jk_cache(wfn_A, wfn_B, sapt_jk, True) core.timer_off("SAPT(DFT):SAPT(DFT):JK") # Electrostatics core.timer_on("SAPT(DFT):SAPT(DFT):elst") elst = sapt_jk_terms.electrostatics(cache, True) data.update(elst) core.timer_off("SAPT(DFT):SAPT(DFT):elst") # Exchange core.timer_on("SAPT(DFT):SAPT(DFT):exch") exch = sapt_jk_terms.exchange(cache, sapt_jk, True) data.update(exch) core.timer_off("SAPT(DFT):SAPT(DFT):exch") # Induction core.timer_on("SAPT(DFT):SAPT(DFT):ind") ind = sapt_jk_terms.induction(cache, sapt_jk, True, sapt_jk_B=sapt_jk_B, maxiter=core.get_option("SAPT", "MAXITER"), conv=core.get_option("SAPT", "D_CONVERGENCE"), Sinf=core.get_option("SAPT", "DO_IND_EXCH_SINF")) data.update(ind) core.timer_off("SAPT(DFT):SAPT(DFT):ind") # Blow away JK object before doing MP2 for memory considerations if cleanup_jk: sapt_jk.finalize() # Dispersion core.timer_on("SAPT(DFT):SAPT(DFT):disp") primary_basis = wfn_A.basisset() core.print_out("\n") aux_basis = core.BasisSet.build(dimer_wfn.molecule(), "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) core.timer_off("SAPT(DFT):SAPT(DFT):disp") # Print out final data core.print_out("\n") core.print_out(print_sapt_dft_summary(data, "SAPT(DFT)")) return data
def scf_iterate(self, e_conv=None, d_conv=None): is_dfjk = core.get_global_option('SCF_TYPE').endswith('DF') verbose = core.get_option('SCF', "PRINT") reference = core.get_option('SCF', "REFERENCE") # self.member_data_ signals are non-local, used internally by c-side fns self.diis_enabled_ = _validate_diis() self.MOM_excited_ = _validate_MOM() self.diis_start_ = core.get_option('SCF', 'DIIS_START') damping_enabled = _validate_damping() soscf_enabled = _validate_soscf() frac_enabled = _validate_frac() efp_enabled = hasattr(self.molecule(), 'EFP') if self.iteration_ < 2: core.print_out(" ==> Iterations <==\n\n") core.print_out( "%s Total Energy Delta E RMS |[F,P]|\n\n" % (" " if is_dfjk else "")) # SCF iterations! SCFE_old = 0.0 SCFE = 0.0 Drms = 0.0 while True: self.iteration_ += 1 diis_performed = False soscf_performed = False self.frac_performed_ = False #self.MOM_performed_ = False # redundant from common_init() self.save_density_and_energy() if efp_enabled: # EFP: Add efp contribution to Fock matrix self.H().copy(self.Horig) global mints_psi4_yo mints_psi4_yo = core.MintsHelper(self.basisset()) Vefp = modify_Fock_induced(self.molecule().EFP, mints_psi4_yo, verbose=verbose - 1) Vefp = core.Matrix.from_array(Vefp) self.H().add(Vefp) SCFE = 0.0 self.clear_external_potentials() core.timer_on("HF: Form G") self.form_G() core.timer_off("HF: Form G") # reset fractional SAD occupation if (self.iteration_ == 0) and self.reset_occ_: self.reset_occupation() upcm = 0.0 if core.get_option('SCF', 'PCM'): calc_type = core.PCM.CalcType.Total if core.get_option("PCM", "PCM_SCF_TYPE") == "SEPARATE": calc_type = core.PCM.CalcType.NucAndEle Dt = self.Da().clone() Dt.add(self.Db()) upcm, Vpcm = self.get_PCM().compute_PCM_terms(Dt, calc_type) SCFE += upcm self.push_back_external_potential(Vpcm) self.set_variable("PCM POLARIZATION ENERGY", upcm) self.set_energies("PCM Polarization", upcm) core.timer_on("HF: Form F") self.form_F() core.timer_off("HF: Form F") if verbose > 3: self.Fa().print_out() self.Fb().print_out() SCFE += self.compute_E() if efp_enabled: global efp_Dt_psi4_yo # EFP: Add efp contribution to energy efp_Dt_psi4_yo = self.Da().clone() efp_Dt_psi4_yo.add(self.Db()) SCFE += self.molecule().EFP.get_wavefunction_dependent_energy() self.set_energies("Total Energy", SCFE) Ediff = SCFE - SCFE_old SCFE_old = SCFE status = [] # We either do SOSCF or DIIS if (soscf_enabled and (self.iteration_ > 3) and (Drms < core.get_option('SCF', 'SOSCF_START_CONVERGENCE'))): Drms = self.compute_orbital_gradient( False, core.get_option('SCF', 'DIIS_MAX_VECS')) diis_performed = False if self.functional().needs_xc(): base_name = "SOKS, nmicro=" else: base_name = "SOSCF, nmicro=" if not _converged(Ediff, Drms, e_conv=e_conv, d_conv=d_conv): nmicro = self.soscf_update( core.get_option('SCF', 'SOSCF_CONV'), core.get_option('SCF', 'SOSCF_MIN_ITER'), core.get_option('SCF', 'SOSCF_MAX_ITER'), core.get_option('SCF', 'SOSCF_PRINT')) if nmicro > 0: # if zero, the soscf call bounced for some reason self.find_occupation() status.append(base_name + str(nmicro)) soscf_performed = True # Stops DIIS else: if verbose > 0: core.print_out( "Did not take a SOSCF step, using normal convergence methods\n" ) soscf_performed = False # Back to DIIS else: # need to ensure orthogonal orbitals and set epsilon status.append(base_name + "conv") core.timer_on("HF: Form C") self.form_C() core.timer_off("HF: Form C") soscf_performed = True # Stops DIIS if not soscf_performed: # Normal convergence procedures if we do not do SOSCF core.timer_on("HF: DIIS") diis_performed = False add_to_diis_subspace = False if self.diis_enabled_ and self.iteration_ >= self.diis_start_: add_to_diis_subspace = True Drms = self.compute_orbital_gradient( add_to_diis_subspace, core.get_option('SCF', 'DIIS_MAX_VECS')) if (self.diis_enabled_ and self.iteration_ >= self.diis_start_ + core.get_option('SCF', 'DIIS_MIN_VECS') - 1): diis_performed = self.diis() if diis_performed: status.append("DIIS") core.timer_off("HF: DIIS") if verbose > 4 and diis_performed: core.print_out(" After DIIS:\n") self.Fa().print_out() self.Fb().print_out() # frac, MOM invoked here from Wfn::HF::find_occupation core.timer_on("HF: Form C") self.form_C() core.timer_off("HF: Form C") if self.MOM_performed_: status.append("MOM") if self.frac_performed_: status.append("FRAC") core.timer_on("HF: Form D") self.form_D() core.timer_off("HF: Form D") core.set_variable("SCF ITERATION ENERGY", SCFE) # After we've built the new D, damp the update if (damping_enabled and self.iteration_ > 1 and Drms > core.get_option('SCF', 'DAMPING_CONVERGENCE')): damping_percentage = core.get_option('SCF', "DAMPING_PERCENTAGE") self.damping_update(damping_percentage * 0.01) status.append("DAMP={}%".format(round(damping_percentage))) if verbose > 3: self.Ca().print_out() self.Cb().print_out() self.Da().print_out() self.Db().print_out() # Print out the iteration core.print_out(" @%s%s iter %3d: %20.14f %12.5e %-11.5e %s\n" % ("DF-" if is_dfjk else "", reference, self.iteration_, SCFE, Ediff, Drms, '/'.join(status))) # if a an excited MOM is requested but not started, don't stop yet if self.MOM_excited_ and not self.MOM_performed_: continue # if a fractional occupation is requested but not started, don't stop yet if frac_enabled and not self.frac_performed_: continue # Call any postiteration callbacks if _converged(Ediff, Drms, e_conv=e_conv, d_conv=d_conv): break if self.iteration_ >= core.get_option('SCF', 'MAXITER'): raise SCFConvergenceError("""SCF iterations""", self.iteration_, self, Ediff, Drms)
def scf_initialize(self): """Specialized initialization, compute integrals and does everything to prepare for iterations""" # Figure out memory distributions # Get memory in terms of doubles total_memory = (core.get_memory() / 8) * core.get_global_option("SCF_MEM_SAFETY_FACTOR") # Figure out how large the DFT collocation matrices are vbase = self.V_potential() if vbase: collocation_size = vbase.grid().collocation_size() if vbase.functional().ansatz() == 1: collocation_size *= 4 # First derivs elif vbase.functional().ansatz() == 2: collocation_size *= 10 # Second derivs else: collocation_size = 0 # Change allocation for collocation matrices based on DFT type scf_type = core.get_global_option('SCF_TYPE').upper() nbf = self.get_basisset("ORBITAL").nbf() naux = self.get_basisset("DF_BASIS_SCF").nbf() if "DIRECT" == scf_type: jk_size = total_memory * 0.1 elif scf_type.endswith('DF'): jk_size = naux * nbf * nbf else: jk_size = nbf**4 # Give remaining to collocation if total_memory > jk_size: collocation_memory = total_memory - jk_size # Give up to 10% to collocation elif (total_memory * 0.1) > collocation_size: collocation_memory = collocation_size else: collocation_memory = total_memory * 0.1 if collocation_memory > collocation_size: collocation_memory = collocation_size # Set constants self.iteration_ = 0 self.memory_jk_ = int(total_memory - collocation_memory) self.memory_collocation_ = int(collocation_memory) # Print out initial docc/socc/etc data if core.get_option('SCF', "PRINT") > 0: core.print_out(" ==> Pre-Iterations <==\n\n") self.print_preiterations() # Initialize EFP efp_enabled = hasattr(self.molecule(), 'EFP') if efp_enabled: # EFP: Set QM system, options, and callback. Display efp geom in [A] efpobj = self.molecule().EFP core.print_out(efpobj.banner()) core.print_out( efpobj.geometry_summary(units_to_bohr=constants.bohr2angstroms)) efpptc, efpcoords, efpopts = get_qm_atoms_opts(self.molecule()) efpobj.set_point_charges(efpptc, efpcoords) efpobj.set_opts(efpopts, label='psi', append='psi') efpobj.set_electron_density_field_fn(field_fn) # Initilize all integratals and perform the first guess if self.attempt_number_ == 1: mints = core.MintsHelper(self.basisset()) if core.get_global_option('RELATIVISTIC') in ['X2C', 'DKH']: mints.set_rel_basisset(self.get_basisset('BASIS_RELATIVISTIC')) mints.one_electron_integrals() self.initialize_jk(self.memory_jk_) if self.V_potential(): self.V_potential().build_collocation_cache( self.memory_collocation_) core.timer_on("HF: Form core H") self.form_H() core.timer_off("HF: Form core H") if efp_enabled: # EFP: Add in permanent moment contribution and cache core.timer_on("HF: Form Vefp") verbose = core.get_option('SCF', "PRINT") Vefp = modify_Fock_permanent(self.molecule(), mints, verbose=verbose - 1) Vefp = core.Matrix.from_array(Vefp) self.H().add(Vefp) Horig = self.H().clone() self.Horig = Horig core.print_out( " QM/EFP: iterating Total Energy including QM/EFP Induction\n" ) core.timer_off("HF: Form Vefp") core.timer_on("HF: Form S/X") self.form_Shalf() core.timer_off("HF: Form S/X") core.timer_on("HF: Guess") self.guess() core.timer_off("HF: Guess") else: # We're reading the orbitals from the previous set of iterations. self.form_D() self.set_energies("Total Energy", self.compute_initial_E()) # turn off VV10 for iterations if core.get_option( 'SCF', "DFT_VV10_POSTSCF") and self.functional().vv10_b() > 0.0: core.print_out(" VV10: post-SCF option active \n \n") self.functional().set_lock(False) self.functional().set_do_vv10(False) self.functional().set_lock(True)
def scf_iterate(self, e_conv=None, d_conv=None): is_dfjk = core.get_global_option('SCF_TYPE').endswith('DF') verbose = core.get_option('SCF', "PRINT") reference = core.get_option('SCF', "REFERENCE") # self.member_data_ signals are non-local, used internally by c-side fns self.diis_enabled_ = self.validate_diis() self.MOM_excited_ = _validate_MOM() self.diis_start_ = core.get_option('SCF', 'DIIS_START') damping_enabled = _validate_damping() soscf_enabled = _validate_soscf() frac_enabled = _validate_frac() efp_enabled = hasattr(self.molecule(), 'EFP') # SCF iterations! SCFE_old = 0.0 Dnorm = 0.0 while True: self.iteration_ += 1 diis_performed = False soscf_performed = False self.frac_performed_ = False #self.MOM_performed_ = False # redundant from common_init() self.save_density_and_energy() if efp_enabled: # EFP: Add efp contribution to Fock matrix self.H().copy(self.Horig) global mints_psi4_yo mints_psi4_yo = core.MintsHelper(self.basisset()) Vefp = modify_Fock_induced(self.molecule().EFP, mints_psi4_yo, verbose=verbose - 1) Vefp = core.Matrix.from_array(Vefp) self.H().add(Vefp) SCFE = 0.0 self.clear_external_potentials() core.timer_on("HF: Form G") self.form_G() core.timer_off("HF: Form G") incfock_performed = hasattr( self.jk(), "do_incfock_iter") and self.jk().do_incfock_iter() upcm = 0.0 if core.get_option('SCF', 'PCM'): calc_type = core.PCM.CalcType.Total if core.get_option("PCM", "PCM_SCF_TYPE") == "SEPARATE": calc_type = core.PCM.CalcType.NucAndEle Dt = self.Da().clone() Dt.add(self.Db()) upcm, Vpcm = self.get_PCM().compute_PCM_terms(Dt, calc_type) SCFE += upcm self.push_back_external_potential(Vpcm) self.set_variable("PCM POLARIZATION ENERGY", upcm) # P::e PCM self.set_energies("PCM Polarization", upcm) upe = 0.0 if core.get_option('SCF', 'PE'): Dt = self.Da().clone() Dt.add(self.Db()) upe, Vpe = self.pe_state.get_pe_contribution(Dt, elec_only=False) SCFE += upe self.push_back_external_potential(Vpe) self.set_variable("PE ENERGY", upe) # P::e PE self.set_energies("PE Energy", upe) core.timer_on("HF: Form F") # SAD: since we don't have orbitals yet, we might not be able # to form the real Fock matrix. Instead, build an initial one if (self.iteration_ == 0) and self.sad_: self.form_initial_F() else: self.form_F() core.timer_off("HF: Form F") if verbose > 3: self.Fa().print_out() self.Fb().print_out() SCFE += self.compute_E() if efp_enabled: global efp_Dt_psi4_yo # EFP: Add efp contribution to energy efp_Dt_psi4_yo = self.Da().clone() efp_Dt_psi4_yo.add(self.Db()) SCFE += self.molecule().EFP.get_wavefunction_dependent_energy() self.set_energies("Total Energy", SCFE) core.set_variable("SCF ITERATION ENERGY", SCFE) Ediff = SCFE - SCFE_old SCFE_old = SCFE status = [] # Check if we are doing SOSCF if (soscf_enabled and (self.iteration_ >= 3) and (Dnorm < core.get_option('SCF', 'SOSCF_START_CONVERGENCE'))): Dnorm = self.compute_orbital_gradient( False, core.get_option('SCF', 'DIIS_MAX_VECS')) diis_performed = False if self.functional().needs_xc(): base_name = "SOKS, nmicro=" else: base_name = "SOSCF, nmicro=" if not _converged(Ediff, Dnorm, e_conv=e_conv, d_conv=d_conv): nmicro = self.soscf_update( core.get_option('SCF', 'SOSCF_CONV'), core.get_option('SCF', 'SOSCF_MIN_ITER'), core.get_option('SCF', 'SOSCF_MAX_ITER'), core.get_option('SCF', 'SOSCF_PRINT')) # if zero, the soscf call bounced for some reason soscf_performed = (nmicro > 0) if soscf_performed: self.find_occupation() status.append(base_name + str(nmicro)) else: if verbose > 0: core.print_out( "Did not take a SOSCF step, using normal convergence methods\n" ) else: # need to ensure orthogonal orbitals and set epsilon status.append(base_name + "conv") core.timer_on("HF: Form C") self.form_C() core.timer_off("HF: Form C") soscf_performed = True # Stops DIIS if not soscf_performed: # Normal convergence procedures if we do not do SOSCF # SAD: form initial orbitals from the initial Fock matrix, and # reset the occupations. The reset is necessary because SAD # nalpha_ and nbeta_ are not guaranteed physical. # From here on, the density matrices are correct. if (self.iteration_ == 0) and self.sad_: self.form_initial_C() self.reset_occupation() self.find_occupation() else: # Run DIIS core.timer_on("HF: DIIS") diis_performed = False add_to_diis_subspace = self.diis_enabled_ and self.iteration_ >= self.diis_start_ Dnorm = self.compute_orbital_gradient( add_to_diis_subspace, core.get_option('SCF', 'DIIS_MAX_VECS')) if add_to_diis_subspace: for engine_used in self.diis(Dnorm): status.append(engine_used) core.timer_off("HF: DIIS") if verbose > 4 and diis_performed: core.print_out(" After DIIS:\n") self.Fa().print_out() self.Fb().print_out() # frac, MOM invoked here from Wfn::HF::find_occupation core.timer_on("HF: Form C") level_shift = core.get_option("SCF", "LEVEL_SHIFT") if level_shift > 0 and Dnorm > core.get_option( 'SCF', 'LEVEL_SHIFT_CUTOFF'): status.append("SHIFT") self.form_C(level_shift) else: self.form_C() core.timer_off("HF: Form C") if self.MOM_performed_: status.append("MOM") if self.frac_performed_: status.append("FRAC") if incfock_performed: status.append("INCFOCK") # Reset occupations if necessary if (self.iteration_ == 0) and self.reset_occ_: self.reset_occupation() self.find_occupation() # Form new density matrix core.timer_on("HF: Form D") self.form_D() core.timer_off("HF: Form D") self.set_variable("SCF ITERATION ENERGY", SCFE) core.set_variable("SCF D NORM", Dnorm) # After we've built the new D, damp the update if (damping_enabled and self.iteration_ > 1 and Dnorm > core.get_option('SCF', 'DAMPING_CONVERGENCE')): damping_percentage = core.get_option('SCF', "DAMPING_PERCENTAGE") self.damping_update(damping_percentage * 0.01) status.append("DAMP={}%".format(round(damping_percentage))) if core.has_option_changed("SCF", "ORBITALS_WRITE"): filename = core.get_option("SCF", "ORBITALS_WRITE") self.to_file(filename) if verbose > 3: self.Ca().print_out() self.Cb().print_out() self.Da().print_out() self.Db().print_out() # Print out the iteration core.print_out( " @%s%s iter %3s: %20.14f %12.5e %-11.5e %s\n" % ("DF-" if is_dfjk else "", reference, "SAD" if ((self.iteration_ == 0) and self.sad_) else self.iteration_, SCFE, Ediff, Dnorm, '/'.join(status))) # if a an excited MOM is requested but not started, don't stop yet # Note that MOM_performed_ just checks initialization, and our convergence measures used the pre-MOM orbitals if self.MOM_excited_ and ((not self.MOM_performed_) or self.iteration_ == core.get_option('SCF', "MOM_START")): continue # if a fractional occupation is requested but not started, don't stop yet if frac_enabled and not self.frac_performed_: continue # Call any postiteration callbacks if not ((self.iteration_ == 0) and self.sad_) and _converged( Ediff, Dnorm, e_conv=e_conv, d_conv=d_conv): break if self.iteration_ >= core.get_option('SCF', 'MAXITER'): raise SCFConvergenceError("""SCF iterations""", self.iteration_, self, Ediff, Dnorm)
def scf_initialize(self): """Specialized initialization, compute integrals and does everything to prepare for iterations""" # Figure out memory distributions # Get memory in terms of doubles total_memory = (core.get_memory() / 8) * core.get_global_option("SCF_MEM_SAFETY_FACTOR") # Figure out how large the DFT collocation matrices are vbase = self.V_potential() if vbase: collocation_size = vbase.grid().collocation_size() if vbase.functional().ansatz() == 1: collocation_size *= 4 # First derivs elif vbase.functional().ansatz() == 2: collocation_size *= 10 # Second derivs else: collocation_size = 0 # Change allocation for collocation matrices based on DFT type jk = _build_jk(self, total_memory) jk_size = jk.memory_estimate() # Give remaining to collocation if total_memory > jk_size: collocation_memory = total_memory - jk_size # Give up to 10% to collocation elif (total_memory * 0.1) > collocation_size: collocation_memory = collocation_size else: collocation_memory = total_memory * 0.1 if collocation_memory > collocation_size: collocation_memory = collocation_size # Set constants self.iteration_ = 0 self.memory_jk_ = int(total_memory - collocation_memory) self.memory_collocation_ = int(collocation_memory) if self.get_print(): core.print_out(" ==> Integral Setup <==\n\n") # Initialize EFP efp_enabled = hasattr(self.molecule(), 'EFP') if efp_enabled: # EFP: Set QM system, options, and callback. Display efp geom in [A] efpobj = self.molecule().EFP core.print_out(efpobj.banner()) core.print_out( efpobj.geometry_summary(units_to_bohr=constants.bohr2angstroms)) efpptc, efpcoords, efpopts = get_qm_atoms_opts(self.molecule()) efpobj.set_point_charges(efpptc, efpcoords) efpobj.set_opts(efpopts, label='psi', append='psi') efpobj.set_electron_density_field_fn(efp_field_fn) # Initialize all integrals and perform the first guess if self.attempt_number_ == 1: mints = core.MintsHelper(self.basisset()) self.initialize_jk(self.memory_jk_, jk=jk) if self.V_potential(): self.V_potential().build_collocation_cache( self.memory_collocation_) core.timer_on("HF: Form core H") self.form_H() core.timer_off("HF: Form core H") if efp_enabled: # EFP: Add in permanent moment contribution and cache core.timer_on("HF: Form Vefp") verbose = core.get_option('SCF', "PRINT") Vefp = modify_Fock_permanent(self.molecule(), mints, verbose=verbose - 1) Vefp = core.Matrix.from_array(Vefp) self.H().add(Vefp) Horig = self.H().clone() self.Horig = Horig core.print_out( " QM/EFP: iterating Total Energy including QM/EFP Induction\n" ) core.timer_off("HF: Form Vefp") core.timer_on("HF: Form S/X") self.form_Shalf() core.timer_off("HF: Form S/X") core.print_out("\n ==> Pre-Iterations <==\n\n") core.timer_on("HF: Guess") self.guess() core.timer_off("HF: Guess") # Print out initial docc/socc/etc data if self.get_print(): lack_occupancy = core.get_local_option('SCF', 'GUESS') in ['SAD'] if core.get_global_option('GUESS') in ['SAD']: lack_occupancy = core.get_local_option('SCF', 'GUESS') in ['AUTO'] self.print_preiterations(small=lack_occupancy) else: self.print_preiterations(small=lack_occupancy) else: # We're reading the orbitals from the previous set of iterations. self.form_D() self.set_energies("Total Energy", self.compute_initial_E()) # turn off VV10 for iterations if core.get_option( 'SCF', "DFT_VV10_POSTSCF") and self.functional().vv10_b() > 0.0: core.print_out(" VV10: post-SCF option active \n \n") self.functional().set_lock(False) self.functional().set_do_vv10(False) self.functional().set_lock(True) # Print iteration header is_dfjk = core.get_global_option('SCF_TYPE').endswith('DF') diis_rms = core.get_option('SCF', 'DIIS_RMS_ERROR') core.print_out(" ==> Iterations <==\n\n") core.print_out( "%s Total Energy Delta E %s |[F,P]|\n\n" % (" " if is_dfjk else "", "RMS" if diis_rms else "MAX"))
def fisapt_compute_energy(self, external_potentials=None): """Computes the FSAPT energy. FISAPT::compute_energy""" # => Header <= self.print_header() # => Zero-th Order Wavefunction <= core.timer_on("FISAPT: Setup") self.localize() self.partition() self.overlap() self.kinetic() self.nuclear() self.coulomb() core.timer_off("FISAPT: Setup") core.timer_on("FISAPT: Monomer SCF") self.scf() core.timer_off("FISAPT: Monomer SCF") self.freeze_core() self.unify() core.timer_on("FISAPT: Subsys E") self.dHF() core.timer_off("FISAPT: Subsys E") # => SAPT0 <= core.timer_on("FISAPT:SAPT:elst") self.elst() core.timer_off("FISAPT:SAPT:elst") core.timer_on("FISAPT:SAPT:exch") self.exch() core.timer_off("FISAPT:SAPT:exch") core.timer_on("FISAPT:SAPT:ind") self.ind() core.timer_off("FISAPT:SAPT:ind") if not core.get_option("FISAPT", "FISAPT_DO_FSAPT"): core.timer_on("FISAPT:SAPT:disp") self.disp( self.matrices(), self.vectors(), True ) # Expensive, only do if needed # unteseted translation of below # self.disp(matrices_, vectors_, true) # Expensive, only do if needed core.timer_off("FISAPT:SAPT:disp") # => F-SAPT0 <= if core.get_option("FISAPT", "FISAPT_DO_FSAPT"): core.timer_on("FISAPT:FSAPT:loc") self.flocalize() core.timer_off("FISAPT:FSAPT:loc") core.timer_on("FISAPT:FSAPT:elst") self.felst() core.timer_off("FISAPT:FSAPT:elst") core.timer_on("FISAPT:FSAPT:exch") self.fexch() core.timer_off("FISAPT:FSAPT:exch") core.timer_on("FISAPT:FSAPT:ind") self.find() core.timer_off("FISAPT:FSAPT:ind") if core.get_option("FISAPT", "FISAPT_DO_FSAPT_DISP"): core.timer_on("FISAPT:FSAPT:disp") self.fdisp() core.timer_off("FISAPT:FSAPT:disp") #else: # # Build Empirical Dispersion # dashD = empirical_dispersion.EmpiricalDispersion(name_hint='SAPT0-D3M') # dashD.print_out() # # Compute -D # Edisp = dashD.compute_energy(core.get_active_molecule()) # core.set_variable('{} DISPERSION CORRECTION ENERGY'.format(dashD.fctldash), Edisp) # Printing # text = [] # text.append(" => {}: Empirical Dispersion <=".format(dashD.fctldash.upper())) # text.append(" ") # text.append(dashD.description) # text.append(dashD.dashlevel_citation.rstrip()) # text.append("\n Empirical Dispersion Energy [Eh] = {:24.16f}\n".format(Edisp)) # text.append('\n') # core.print_out('\n'.join(text)) self.fdrop(external_potentials) # => Scalar-Field Analysis <= if core.get_option("FISAPT", "FISAPT_DO_PLOT"): core.timer_on("FISAPT:FSAPT:cubeplot") self.plot() core.timer_off("FISAPT:FSAPT:cubeplot") # => Summary <= self.print_trailer()
def fisapt_compute_energy(self): """Computes the FSAPT energy. FISAPT::compute_energy""" # => Header <= self.print_header() # => Zero-th Order Wavefunction <= core.timer_on("FISAPT: Setup") self.localize() self.partition() self.overlap() self.kinetic() self.nuclear() self.coulomb() core.timer_off("FISAPT: Setup") core.timer_on("FISAPT: Monomer SCF") self.scf() core.timer_off("FISAPT: Monomer SCF") self.freeze_core() self.unify() core.timer_on("FISAPT: Subsys E") self.dHF() core.timer_off("FISAPT: Subsys E") # => SAPT0 <= core.timer_on("FISAPT:SAPT:elst") self.elst() core.timer_off("FISAPT:SAPT:elst") core.timer_on("FISAPT:SAPT:exch") self.exch() core.timer_off("FISAPT:SAPT:exch") core.timer_on("FISAPT:SAPT:ind") self.ind() core.timer_off("FISAPT:SAPT:ind") if not core.get_option("FISAPT", "FISAPT_DO_FSAPT"): core.timer_on("FISAPT:SAPT:disp") self.disp(matrices_, vectors_, true) # Expensive, only do if needed core.timer_off("FISAPT:SAPT:disp") # => F-SAPT0 <= if core.get_option("FISAPT", "FISAPT_DO_FSAPT"): core.timer_on("FISAPT:FSAPT:loc") self.flocalize() core.timer_off("FISAPT:FSAPT:loc") core.timer_on("FISAPT:FSAPT:elst") self.felst() core.timer_off("FISAPT:FSAPT:elst") core.timer_on("FISAPT:FSAPT:exch") self.fexch() core.timer_off("FISAPT:FSAPT:exch") core.timer_on("FISAPT:FSAPT:ind") self.find() core.timer_off("FISAPT:FSAPT:ind") core.timer_on("FISAPT:FSAPT:disp") self.fdisp() core.timer_off("FISAPT:FSAPT:disp") self.fdrop() # => Scalar-Field Analysis <= if core.get_option("FISAPT", "FISAPT_DO_PLOT"): core.timer_on("FISAPT:FSAPT:cubeplot") self.plot() core.timer_off("FISAPT:FSAPT:cubeplot") # => Summary <= self.print_trailer()
def scf_iterate(self, e_conv=None, d_conv=None): is_dfjk = core.get_global_option('SCF_TYPE').endswith('DF') verbose = core.get_option('SCF', "PRINT") reference = core.get_option('SCF', "REFERENCE") # self.member_data_ signals are non-local, used internally by c-side fns self.diis_enabled_ = _validate_diis() self.MOM_excited_ = _validate_MOM() self.diis_start_ = core.get_option('SCF', 'DIIS_START') damping_enabled = _validate_damping() soscf_enabled = _validate_soscf() frac_enabled = _validate_frac() efp_enabled = hasattr(self.molecule(), 'EFP') if self.iteration_ < 2: core.print_out(" ==> Iterations <==\n\n") core.print_out("%s Total Energy Delta E RMS |[F,P]|\n\n" % (" " if is_dfjk else "")) # SCF iterations! SCFE_old = 0.0 SCFE = 0.0 Drms = 0.0 while True: self.iteration_ += 1 diis_performed = False soscf_performed = False self.frac_performed_ = False #self.MOM_performed_ = False # redundant from common_init() self.save_density_and_energy() if efp_enabled: # EFP: Add efp contribution to Fock matrix self.H().copy(self.Horig) global mints mints = core.MintsHelper(self.basisset()) Vefp = modify_Fock_induced(self.molecule().EFP, mints, verbose=verbose-1) Vefp = core.Matrix.from_array(Vefp) self.H().add(Vefp) SCFE = 0.0 self.clear_external_potentials() core.timer_on("HF: Form G") self.form_G() core.timer_off("HF: Form G") # reset fractional SAD occupation if (self.iteration_ == 0) and self.reset_occ_: self.reset_occupation() upcm = 0.0 if core.get_option('SCF', 'PCM'): calc_type = core.PCM.CalcType.Total if core.get_option("PCM", "PCM_SCF_TYPE") == "SEPARATE": calc_type = core.PCM.CalcType.NucAndEle Dt = self.Da().clone() Dt.add(self.Db()) upcm, Vpcm = self.get_PCM().compute_PCM_terms(Dt, calc_type) SCFE += upcm self.push_back_external_potential(Vpcm) self.set_variable("PCM POLARIZATION ENERGY", upcm) self.set_energies("PCM Polarization", upcm) core.timer_on("HF: Form F") self.form_F() core.timer_off("HF: Form F") if verbose > 3: self.Fa().print_out() self.Fb().print_out() SCFE += self.compute_E() if efp_enabled: global efp_Dt # EFP: Add efp contribution to energy efp_Dt = self.Da().clone() efp_Dt.add(self.Db()) SCFE += self.molecule().EFP.get_wavefunction_dependent_energy() self.set_energies("Total Energy", SCFE) Ediff = SCFE - SCFE_old SCFE_old = SCFE status = [] # We either do SOSCF or DIIS if (soscf_enabled and (self.iteration_ > 3) and (Drms < core.get_option('SCF', 'SOSCF_START_CONVERGENCE'))): Drms = self.compute_orbital_gradient(False, core.get_option('SCF', 'DIIS_MAX_VECS')) diis_performed = False if self.functional().needs_xc(): base_name = "SOKS, nmicro=" else: base_name = "SOSCF, nmicro=" if not _converged(Ediff, Drms, e_conv=e_conv, d_conv=d_conv): nmicro = self.soscf_update( core.get_option('SCF', 'SOSCF_CONV'), core.get_option('SCF', 'SOSCF_MIN_ITER'), core.get_option('SCF', 'SOSCF_MAX_ITER'), core.get_option('SCF', 'SOSCF_PRINT')) if nmicro > 0: # if zero, the soscf call bounced for some reason self.find_occupation() status.append(base_name + str(nmicro)) soscf_performed = True # Stops DIIS else: if verbose > 0: core.print_out("Did not take a SOSCF step, using normal convergence methods\n") soscf_performed = False # Back to DIIS else: # need to ensure orthogonal orbitals and set epsilon status.append(base_name + "conv") core.timer_on("HF: Form C") self.form_C() core.timer_off("HF: Form C") soscf_performed = True # Stops DIIS if not soscf_performed: # Normal convergence procedures if we do not do SOSCF core.timer_on("HF: DIIS") diis_performed = False add_to_diis_subspace = False if self.diis_enabled_ and self.iteration_ >= self.diis_start_: add_to_diis_subspace = True Drms = self.compute_orbital_gradient(add_to_diis_subspace, core.get_option('SCF', 'DIIS_MAX_VECS')) if (self.diis_enabled_ and self.iteration_ >= self.diis_start_ + core.get_option('SCF', 'DIIS_MIN_VECS') - 1): diis_performed = self.diis() if diis_performed: status.append("DIIS") core.timer_off("HF: DIIS") if verbose > 4 and diis_performed: core.print_out(" After DIIS:\n") self.Fa().print_out() self.Fb().print_out() # frac, MOM invoked here from Wfn::HF::find_occupation core.timer_on("HF: Form C") self.form_C() core.timer_off("HF: Form C") if self.MOM_performed_: status.append("MOM") if self.frac_performed_: status.append("FRAC") core.timer_on("HF: Form D") self.form_D() core.timer_off("HF: Form D") core.set_variable("SCF ITERATION ENERGY", SCFE) # After we've built the new D, damp the update if (damping_enabled and self.iteration_ > 1 and Drms > core.get_option('SCF', 'DAMPING_CONVERGENCE')): damping_percentage = core.get_option('SCF', "DAMPING_PERCENTAGE") self.damping_update(damping_percentage * 0.01) status.append("DAMP={}%".format(round(damping_percentage))) if verbose > 3: self.Ca().print_out() self.Cb().print_out() self.Da().print_out() self.Db().print_out() # Print out the iteration core.print_out(" @%s%s iter %3d: %20.14f %12.5e %-11.5e %s\n" % ("DF-" if is_dfjk else "", reference, self.iteration_, SCFE, Ediff, Drms, '/'.join(status))) # if a an excited MOM is requested but not started, don't stop yet if self.MOM_excited_ and not self.MOM_performed_: continue # if a fractional occupation is requested but not started, don't stop yet if frac_enabled and not self.frac_performed_: continue # Call any postiteration callbacks if _converged(Ediff, Drms, e_conv=e_conv, d_conv=d_conv): break if self.iteration_ >= core.get_option('SCF', 'MAXITER'): raise ConvergenceError("""SCF iterations""", self.iteration_)
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') core.timer_on("SAPT(DFT): Dimer SCF") 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") core.timer_off("SAPT(DFT): Dimer SCF") core.timer_on("SAPT(DFT): Monomer A SCF") 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.timer_off("SAPT(DFT): Monomer A SCF") core.timer_on("SAPT(DFT): Monomer B SCF") 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) core.timer_off("SAPT(DFT): Monomer B SCF") # 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 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") # Build cache hf_cache = sapt_jk_terms.build_sapt_jk_cache(hf_wfn_A, hf_wfn_B, sapt_jk, True) # Electrostatics core.timer_on("SAPT(DFT):SAPT:elst") elst = sapt_jk_terms.electrostatics(hf_cache, True) hf_data.update(elst) core.timer_off("SAPT(DFT):SAPT:elst") # Exchange core.timer_on("SAPT(DFT):SAPT:exch") exch = sapt_jk_terms.exchange(hf_cache, sapt_jk, True) hf_data.update(exch) core.timer_off("SAPT(DFT):SAPT:exch") # Induction core.timer_on("SAPT(DFT):SAPT:ind") 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) core.timer_off("SAPT(DFT):SAPT: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 core.timer_on("SAPT(DFT): Monomer A DFT") 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) 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) core.timer_off("SAPT(DFT): Monomer A DFT") # Compute Monomer B wavefunction core.timer_on("SAPT(DFT): Monomer B DFT") 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) core.timer_off("SAPT(DFT): Monomer B DFT") # 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 sapt_dft(dimer_wfn, wfn_A, wfn_B, sapt_jk=None, sapt_jk_B=None, data=None, print_header=True, cleanup_jk=True): """ The primary SAPT(DFT) algorithm to compute the interaction energy once the wavefunctions have been built. Example ------- dimer = psi4.geometry(''' Ne -- Ar 1 6.5 units bohr ''') psi4.set_options({"BASIS": "aug-cc-pVDZ"}) # Prepare the fragments sapt_dimer, monomerA, monomerB = psi4.proc_util.prepare_sapt_molecule(sapt_dimer, "dimer") # Run the first monomer set DFT_GRAC_SHIFT 0.203293 wfnA, energyA = psi4.energy("PBE0", monomer=monomerA, return_wfn=True) # Run the second monomer set DFT_GRAC_SHIFT 0.138264 wfnB, energyB = psi4.energy("PBE0", monomer=monomerB, return_wfn=True) # Build the dimer wavefunction wfnD = psi4.core.Wavefunction.build(sapt_dimer) # Compute SAPT(DFT) from the provided wavefunctions data = psi4.procrouting.sapt.sapt_dft(wfnD, wfnA, wfnB) """ # Handle the input options core.timer_on("SAPT(DFT):SAPT(DFT):JK") if print_header: sapt_dft_header() if sapt_jk is None: core.print_out("\n => Building SAPT JK object <= \n\n") sapt_jk = core.JK.build(dimer_wfn.basisset()) sapt_jk.set_do_J(True) sapt_jk.set_do_K(True) if wfn_A.functional().is_x_lrc(): sapt_jk.set_do_wK(True); sapt_jk.set_omega(wfn_A.functional().x_omega()); sapt_jk.initialize() sapt_jk.print_header() if wfn_B.functional().is_x_lrc() and (wfn_A.functional().x_omega() != wfn_B.functional().x_omega()): core.print_out(" => Monomer B: Building SAPT JK object <= \n\n") core.print_out(" Reason: MonomerA Omega != MonomerB Omega\n\n") sapt_jk_B = core.JK.build(dimer_wfn.basisset()) sapt_jk_B.set_do_J(True) sapt_jk_B.set_do_K(True) sapt_jk_B.set_do_wK(True); sapt_jk_B.set_omega(wfn_B.functional().x_omega()); sapt_jk_B.initialize() sapt_jk_B.print_header() else: sapt_jk.set_do_K(True) if data is None: data = {} # Build SAPT cache cache = sapt_jk_terms.build_sapt_jk_cache(wfn_A, wfn_B, sapt_jk, True) core.timer_off("SAPT(DFT):SAPT(DFT):JK") # Electrostatics core.timer_on("SAPT(DFT):SAPT(DFT):elst") elst = sapt_jk_terms.electrostatics(cache, True) data.update(elst) core.timer_off("SAPT(DFT):SAPT(DFT):elst") # Exchange core.timer_on("SAPT(DFT):SAPT(DFT):exch") exch = sapt_jk_terms.exchange(cache, sapt_jk, True) data.update(exch) core.timer_off("SAPT(DFT):SAPT(DFT):exch") # Induction core.timer_on("SAPT(DFT):SAPT(DFT):ind") ind = sapt_jk_terms.induction( cache, sapt_jk, True, sapt_jk_B=sapt_jk_B, maxiter=core.get_option("SAPT", "MAXITER"), conv=core.get_option("SAPT", "D_CONVERGENCE"), Sinf=core.get_option("SAPT", "DO_IND_EXCH_SINF")) data.update(ind) core.timer_off("SAPT(DFT):SAPT(DFT):ind") # Blow away JK object before doing MP2 for memory considerations if cleanup_jk: sapt_jk.finalize() # Dispersion core.timer_on("SAPT(DFT):SAPT(DFT):disp") primary_basis = wfn_A.basisset() core.print_out("\n") aux_basis = core.BasisSet.build(dimer_wfn.molecule(), "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) core.timer_off("SAPT(DFT):SAPT(DFT):disp") # Print out final data core.print_out("\n") core.print_out(print_sapt_dft_summary(data, "SAPT(DFT)")) return data
def scf_initialize(self): """Specialized initialization, compute integrals and does everything to prepare for iterations""" # Figure out memory distributions # Get memory in terms of doubles total_memory = (core.get_memory() / 8) * core.get_global_option("SCF_MEM_SAFETY_FACTOR") # Figure out how large the DFT collocation matrices are vbase = self.V_potential() if vbase: collocation_size = vbase.grid().collocation_size() if vbase.functional().ansatz() == 1: collocation_size *= 4 # First derivs elif vbase.functional().ansatz() == 2: collocation_size *= 10 # Second derivs else: collocation_size = 0 # Change allocation for collocation matrices based on DFT type jk = _build_jk(self, total_memory) jk_size = jk.memory_estimate() # Give remaining to collocation if total_memory > jk_size: collocation_memory = total_memory - jk_size # Give up to 10% to collocation elif (total_memory * 0.1) > collocation_size: collocation_memory = collocation_size else: collocation_memory = total_memory * 0.1 if collocation_memory > collocation_size: collocation_memory = collocation_size # Set constants self.iteration_ = 0 self.memory_jk_ = int(total_memory - collocation_memory) self.memory_collocation_ = int(collocation_memory) # Print out initial docc/socc/etc data if self.get_print(): core.print_out(" ==> Pre-Iterations <==\n\n") self.print_preiterations() if self.get_print(): core.print_out(" ==> Integral Setup <==\n\n") # Initialize EFP efp_enabled = hasattr(self.molecule(), 'EFP') if efp_enabled: # EFP: Set QM system, options, and callback. Display efp geom in [A] efpobj = self.molecule().EFP core.print_out(efpobj.banner()) core.print_out(efpobj.geometry_summary(units_to_bohr=constants.bohr2angstroms)) efpptc, efpcoords, efpopts = get_qm_atoms_opts(self.molecule()) efpobj.set_point_charges(efpptc, efpcoords) efpobj.set_opts(efpopts, label='psi', append='psi') efpobj.set_electron_density_field_fn(field_fn) # Initilize all integratals and perform the first guess if self.attempt_number_ == 1: mints = core.MintsHelper(self.basisset()) if core.get_global_option('RELATIVISTIC') in ['X2C', 'DKH']: mints.set_rel_basisset(self.get_basisset('BASIS_RELATIVISTIC')) mints.one_electron_integrals() self.initialize_jk(self.memory_jk_, jk=jk) if self.V_potential(): self.V_potential().build_collocation_cache(self.memory_collocation_) core.timer_on("HF: Form core H") self.form_H() core.timer_off("HF: Form core H") if efp_enabled: # EFP: Add in permanent moment contribution and cache core.timer_on("HF: Form Vefp") verbose = core.get_option('SCF', "PRINT") Vefp = modify_Fock_permanent(self.molecule(), mints, verbose=verbose-1) Vefp = core.Matrix.from_array(Vefp) self.H().add(Vefp) Horig = self.H().clone() self.Horig = Horig core.print_out(" QM/EFP: iterating Total Energy including QM/EFP Induction\n") core.timer_off("HF: Form Vefp") core.timer_on("HF: Form S/X") self.form_Shalf() core.timer_off("HF: Form S/X") core.timer_on("HF: Guess") self.guess() core.timer_off("HF: Guess") else: # We're reading the orbitals from the previous set of iterations. self.form_D() self.set_energies("Total Energy", self.compute_initial_E()) # turn off VV10 for iterations if core.get_option('SCF', "DFT_VV10_POSTSCF") and self.functional().vv10_b() > 0.0: core.print_out(" VV10: post-SCF option active \n \n") self.functional().set_lock(False) self.functional().set_do_vv10(False) self.functional().set_lock(True)