def scf_compute_energy(self): """Base class Wavefunction requires this function. Here it is simply a wrapper around initialize(), iterations(), finalize_energy(). It returns the SCF energy computed by finalize_energy(). """ if core.get_option('SCF', 'DF_SCF_GUESS') and (core.get_global_option('SCF_TYPE') == 'DIRECT'): # speed up DIRECT algorithm (recomputes full (non-DF) integrals # each iter) by first converging via fast DF iterations, then # fully converging in fewer slow DIRECT iterations. aka Andy trick 2.0 core.print_out(" Starting with a DF guess...\n\n") with p4util.OptionsStateCM(['SCF_TYPE']): core.set_global_option('SCF_TYPE', 'DF') self.initialize() try: self.iterations() except SCFConvergenceError: self.finalize() raise SCFConvergenceError("""SCF DF preiterations""", self.iteration_, self, 0, 0) core.print_out("\n DF guess converged.\n\n") # reset the DIIS & JK objects in prep for DIRECT if self.initialized_diis_manager_: self.diis_manager().reset_subspace() self.initialize_jk(self.memory_jk_) else: self.initialize() try: self.iterations() except SCFConvergenceError as e: if core.get_option("SCF", "FAIL_ON_MAXITER"): core.print_out(" Failed to converge.\n") # energy = 0.0 # A P::e fn to either throw or protest upon nonconvergence # die_if_not_converged() raise e else: core.print_out(" Energy and/or wave function did not converge, but proceeding anyway.\n\n") else: core.print_out(" Energy and wave function converged.\n\n") scf_energy = self.finalize_energy() return scf_energy
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_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)