Source code for storq.vasp.setters

import numpy as np
import os
from storq.vasp.readers import read_enmax
from ase.calculators.calculator import equal


[docs]class Setters:
[docs] def set(self, **kwargs): """Set parameters with keyword=value pairs. A few special kwargs are handled separately to expand them prior to setting the parameters. This is done to enable one set to track changes. Parameters ---------- **kwargs Can be any calculator keyword. """ if "xc" in kwargs: kwargs.update(self.set_xc_dict(kwargs["xc"])) if "ispin" in kwargs: kwargs.update(self.set_ispin_dict(kwargs["ispin"])) if "ldau_luj" in kwargs: kwargs.update(self.set_ldau_luj_dict(kwargs["ldau_luj"])) if "kpts" in kwargs: if not isinstance(kwargs["kpts"], dict): kwargs["kpts"] = self.set_kpts_dict(kwargs["kpts"]) else: kwargs["kpts"].update(self.set_kpts_dict(kwargs["kpts"])) original_params = self.parameters changed_parameters = {} for key, value in kwargs.items(): oldvalue = self.parameters.get(key) if key not in self.parameters or not equal(value, oldvalue): changed_parameters[key] = value self.parameters[key] = value # If we are implementing special setups, the ppp_list needs # to be updated so the POSCAR and POTCAR can be written correctly. if "setups" in changed_parameters.keys() and self.atoms is not None: self.sort_atoms(self.atoms) # we don't consider None values to be changed if the keyword was # not originally in the parameters. cp = { k: v for k, v in changed_parameters.items() if v is not None and k not in original_params } if cp != {}: self.results = {} if len(changed_parameters) > 0: self.state = self.NEW
return changed_parameters
[docs] def set_atoms(self, atoms): """ Set the atoms attribute. This is called by the set_calculator method of ASE Atoms objects. """ atoms.pbc = [True, True, True] # if we pass an atoms object with different atomic numbers # or counts the we should do a brand new calculation if not ( equal(atoms.numbers, self.atoms.numbers) and (atoms.pbc == self.atoms.pbc).all() ): self.remove(self.directory, target="output") self.sort_atoms(atoms) self.state == self.NEW else:
self.sort_atoms(atoms)
[docs] def set_ispin_dict(self, val): """Returns dictionary of changes for ispin change. Parameters ---------- val TODO Returns ------- dict TODO """ # there are two ways to get magmom in. # 1. if you use magmom as a keyword, they are used. # 2. if you set magmom on each atom in an Atoms object and do not use # magmom then we use the atoms magmom, if we have ispin=2 set. # we set lorbit to 11 if ispin=2 so we can get the individual moments. if val is None: d = {} for key in ["ispin", "magmom", "lorbit"]: if key in self.parameters: d[key] = None elif val == 1: d = {"ispin": 1} if "magmom" in self.parameters: d["magmom"] = None elif val == 2: d = {"ispin": 2} if "magmom" not in self.parameters: d["magmom"] = [atom.magmom for atom in self.atoms[self.resort]] # print out individual magnetic moments. if "lorbit" not in self.parameters: d["lorbit"] = 11
return d
[docs] def set_rwigs_dict(self, val): """Return rwigs parameters.""" d = {} if val is None: d["rwigs"] = None d["lorbit"] = None else: # val is a dictionary {sym: rwigs} # rwigs needs to be in the order of the potcars d["rwigs"] = [val[x[0]] for x in self.ppp_list]
return d
[docs] def set_ldau_luj_dict(self, val): """Set the ldau_luj parameters.""" if "setups" in self.parameters: raise Exception("setups and ldau_luj is not supported.") if not hasattr(self, "ppp_list"): atoms = self.get_atoms() self.sort_atoms(atoms) if val is not None: atom_types = [ x[0] if isinstance(x[0], str) else self.atoms[x[0]].symbol for x in self.ppp_list ] d = {} d["ldaul"] = [val[sym]["L"] for sym in atom_types] d["ldauu"] = [val[sym]["U"] for sym in atom_types] d["ldauj"] = [val[sym]["J"] for sym in atom_types] return d else: d = {} d["ldaul"] = None d["ldauu"] = None d["ldauj"] = None
return d
[docs] def set_xc_dict(self, val): """Set xc parameter. Adds all the _xc_defaults flags for the chosen xc. """ d = {"xc": val.lower()} oxc = self.parameters.get("xc", None) if oxc: for key in self._xc_defaults[oxc.lower()]: if key in self.parameters: d[key] = None d.update(self._xc_defaults[val.lower()])
return d
[docs] def set_kpts_dict(self, val): """ Set the kpts dict. """ kpts_tmp = self._default_parameters["kpts"].copy() if isinstance(val, list) and len(val) == 3: kpts_tmp.update({"size": val}) elif isinstance(val, dict): if "size" not in val: kpts_tmp.pop("size", None) # remove conflicting param kpts_tmp.update(val)
return kpts_tmp
[docs] def set_number_of_bands(self, factor=1.0): """Convenience function to compute and set the number of bands. Uses the same rule as VASP does internally, which looks roughly like nbands = int(nelectrons/2 + factor*nions/2). The difference is that this method allows the specification of a scaling factor for the term proportional to the number of ions. This can be useful for e.g., transition metals where more bands need to be added sometimes (factor=2 can be required). Parameters ---------- factor : float Multiplicative scaling factor for the number of bands. Returns ------- int The new number of bands. """ nelect = self.get_valence_electrons() natoms = len(self.atoms) nbands = int( max(round(nelect + 2) / 2 + factor * max(natoms // 2, 3), int(0.6 * nelect)) ) if self.parameters.get("npar", None): nbands = ((nbands + npar - 1) // npar) * npar
self.set(nbands=nbands)
[docs] def set_encut(self, factor=1.3): """Heuristic for setting the PW cutoff. Uses the formula factor*max(ENMAX) where the maximum is taken over all POTCARs involved. Parameters ---------- factor : float Multiplicative scaling factor for the eneryg cutoff. """ enmax = 0 for _, pfile, _ in self.ppp_list: pfile = os.path.join(self.conf["vasp_pp_path"], pfile) enmax = max(read_enmax(pfile), enmax) enmax *= factor
self.set(encut=enmax)