def activate_robust_minimization(self): """ Method to modify the set to use more robust SCF minimization technique """ ot = OrbitalTransformation( minimizer="CG", preconditioner="FULL_ALL", algorithm="STRICT", energy_gap=0.05, linesearch="3PNT", ) self.update({"FORCE_EVAL": {"DFT": {"SCF": {"OT": ot}}}})
def activate_very_strict_minimization(self): """ Method to modify the set to use very strict SCF minimization scheme :return: """ ot = OrbitalTransformation( minimizer="CG", preconditioner="FULL_ALL", algorithm="STRICT", energy_gap=0.05, linesearch="GOLD", ) self.update({"FORCE_EVAL": {"DFT": {"SCF": {"OT": ot}}}})
def activate_fast_minimization(self, on): """ Method to modify the set to use fast SCF minimization. """ if on: ot = OrbitalTransformation( minimizer="DIIS", preconditioner="FULL_ALL", algorithm="IRAC", energy_gap=0.01, linesearch="2PNT", ) self.update({"FORCE_EVAL": {"DFT": {"SCF": {"OT": ot}}}})
def __init__( self, structure: Union[Structure, Molecule], ot: bool = True, band_gap: float = 0.01, eps_default: float = 1e-12, eps_scf: float = 1e-7, max_scf: Union[int, None] = None, minimizer: str = "DIIS", preconditioner: str = "FULL_ALL", algorithm: str = "STRICT", linesearch: str = "2PNT", cutoff: int = 1200, rel_cutoff: int = 80, ngrids: int = 5, progression_factor: int = 3, override_default_params: Dict = {}, wfn_restart_file_name: str = None, kpoints: Union[Kpoints, None] = None, smearing: bool = False, **kwargs ): """ Args: structure: Pymatgen structure or molecule object ot (bool): Whether or not to use orbital transformation method for matrix diagonalization. OT is the flagship scf solver of CP2K, and will provide huge speed-ups for this part of the calculation, but the system must have a band gap for OT to be used (higher band-gap --> faster convergence). Band gap is also used by the preconditioner for OT, and should be set as a value SMALLER than the true band gap to get good efficiency. Generally, this parameter does not need to be changed from default of 0.01 band_gap (float): The band gap can also be specified in order to determine if ot should be turned on. eps_default (float): Replaces all EPS_XX Keywords in the DFT section (NOT its subsections!) to have this value, ensuring an overall accuracy of at least this much. eps_scf (float): The convergence criteria for leaving the SCF loop in Hartrees. Default is 1e-7. Should ensure reasonable results for all properties. Smaller than 1e-7 is generally not needed unless you need very high precision. 1e-6 may be used for difficult systems, and should still give reasonable results for most properties. max_scf (int): The max number of SCF cycles before terminating the solver. NOTE: With the OT solver, this corresponds to the max number of INNER scf loops, and then the outer loops are set with outer_max_scf, while with diagnolization it corresponds to the overall (INNER*OUTER) number of SCF steps, with the inner loop limit set by minimizer (str): The minimization scheme. DIIS can be as much as 50% faster than the more robust conjugate gradient method, and so it is chosen as default. Switch to CG if dealing with a difficult system. preconditioner (str): Preconditioner for the OT method. FULL_ALL is the most reliable, and is the default. Though FULL_SINGLE_INVERSE has faster convergence according to our internal tests. Should only change from theses two when simulation cell gets to be VERY large, in which case FULL_KINETIC might be preferred. cutoff (int): Cutoff energy (in Ry) for the finest level of the multigrid. A high cutoff will allow you to have very accurate calculations PROVIDED that REL_CUTOFF is appropriate. rel_cutoff (int): This cutoff decides how the Guassians are mapped onto the different levels of the multigrid. From CP2K: A Gaussian is mapped onto the coarsest level of the multi-grid, on which the function will cover number of grid points greater than or equal to the number of grid points will cover on a reference grid defined by REL_CUTOFF. progression_factor (int): Divisor of CUTOFF to get the cutoff for the next level of the multigrid. Takeaway for the cutoffs: https://www.cp2k.org/howto:converging_cutoff If CUTOFF is too low, then all grids will be coarse and the calculation may become inaccurate; and if REL_CUTOFF is too low, then even if you have a high CUTOFF, all Gaussians will be mapped onto the coarsest level of the multi-grid, and thus the effective integration grid for the calculation may still be too coarse. """ super().__init__(structure, **kwargs) self.structure = structure self.ot = ot self.band_gap = band_gap self.eps_default = eps_default self.eps_scf = eps_scf self.max_scf = max_scf self.minimizer = minimizer self.preconditioner = preconditioner self.algorithm = algorithm self.linesearch = linesearch self.cutoff = cutoff self.rel_cutoff = rel_cutoff self.ngrids = ngrids self.progression_factor = progression_factor self.override_default_params = override_default_params self.wfn_restart_file_name = wfn_restart_file_name self.kpoints = kpoints self.smearing = smearing self.kwargs = kwargs # Build the QS Section qs = QS(eps_default=eps_default) max_scf = ( max_scf if max_scf else 20 if ot else 400 ) # If ot, max_scf is for inner loop scf = Scf(eps_scf=eps_scf, max_scf=max_scf, subsections={}) # If there's a band gap, use OT, else use Davidson if ot: if band_gap <= 0: warnings.warn( "Orbital Transformation method is being used for" "a system without a bandgap. OT can have very poor" "convergence for metallic systems, proceed with caution.", UserWarning, ) scf.insert( OrbitalTransformation( minimizer=minimizer, preconditioner=preconditioner, energy_gap=band_gap, algorithm=algorithm, linesearch=linesearch, ) ) scf.insert( Section( "OUTER_SCF", subsections={}, keywords={ "MAX_SCF": Keyword("MAX_SCF", kwargs.get("outer_max_scf", 20)), "EPS_SCF": Keyword( "EPS_SCF", kwargs.get("outer_eps_scf", eps_scf) ), }, ) ) else: scf.insert(Section("DIAGONALIZATION", subsections={})) mixing_kwds = { "METHOD": Keyword("METHOD", "BROYDEN_MIXING"), "ALPHA": Keyword("ALPHA", 0.2), "NBUFFER": Keyword("NBUFFER", 5), } mixing = Section("MIXING", keywords=mixing_kwds, subsections=None) scf.insert(mixing) davidson_kwds = {"PRECONDITIONER": Keyword("PRECONDITIONER", "FULL_ALL")} davidson = Section("DAVIDSON", keywords=davidson_kwds, subsections=None) scf["DIAGONALIZATION"].insert(davidson) # Create the multigrid for FFTs mgrid = Mgrid( cutoff=cutoff, rel_cutoff=rel_cutoff, ngrids=ngrids, progression_factor=progression_factor, ) # Set the DFT calculation with global parameters dft = Dft( MULTIPLICITY=self.multiplicity, CHARGE=self.charge, basis_set_filenames=self.basis_set_file_names, potential_filename=self.potential_file_name, subsections={"QS": qs, "SCF": scf, "MGRID": mgrid}, wfn_restart_file_name=wfn_restart_file_name, ) if kpoints: dft.insert(Kpoints.from_kpoints(kpoints)) if smearing or (band_gap <= 0.0): scf.kwargs["ADDED_MOS"] = 100 scf["ADDED_MOS"] = 100 # TODO: how to grab the appropriate number? scf.insert(Smear()) # Create subsections and insert into them self["FORCE_EVAL"].insert(dft) xc_functional = XC_FUNCTIONAL(functional=kwargs.get("functional", "PBE")) xc = Section("XC", subsections={"XC_FUNCTIONAL": xc_functional}) self["FORCE_EVAL"]["DFT"].insert(xc) self["FORCE_EVAL"]["DFT"].insert(Section("PRINT", subsections={})) if isinstance(structure, Molecule): self.activate_nonperiodic() if kwargs.get("print_pdos", True): self.print_pdos() if kwargs.get("print_ldos", False): self.print_ldos() if kwargs.get("print_mo_cubes", True): self.print_mo_cubes() if kwargs.get("print_hartree_potential", False): self.print_hartree_potential() if kwargs.get("print_e_density", False): self.print_e_density() self.update(self.override_default_params)