Exemple #1
0
class PPModel(AbivarAble, PMGSONable):
    """
    Parameters defining the plasmon-pole technique.
    The common way to instanciate a PPModel object is via the class method PPModel.as_ppmodel(string)
    """
    _mode2ppmodel = {
        "noppmodel": 0,
        "godby"    : 1,
        "hybersten": 2,
        "linden"   : 3,
        "farid"    : 4,
    }

    modes = Enum(k for k in _mode2ppmodel)

    @classmethod
    def as_ppmodel(cls, obj):
        """
        Constructs an instance of PPModel from obj.

        Accepts obj in the form:
            * PPmodel instance
            * string. e.g "godby:12.3 eV", "linden".
        """
        if isinstance(obj, cls):
            return obj

        # obj is a string
        if ":" not in obj:
            mode, plasmon_freq = obj, None
        else:
            # Extract mode and plasmon_freq
            mode, plasmon_freq = obj.split(":")
            try:
                plasmon_freq = float(plasmon_freq)
            except ValueError:
                plasmon_freq, unit = plasmon_freq.split()
                plasmon_freq = units.Energy(float(plasmon_freq), unit).to("Ha")

        return cls(mode=mode, plasmon_freq=plasmon_freq)

    def __init__(self, mode="godby", plasmon_freq=None):
        assert mode in PPModel.modes
        self.mode = mode
        self.plasmon_freq = plasmon_freq

    def __eq__(self, other):
        if other is None:
            return False
        else:
            if self.mode != other.mode:
                return False

            if self.plasmon_freq is None:
                return other.plasmon_freq is None
            else:
                return np.allclose(self.plasmon_freq, other.plasmon_freq)

    def __ne__(self, other):
        return not self == other

    def __bool__(self):
        return self.mode != "noppmodel"

    # py2 old version
    __nonzero__ = __bool__

    def __repr__(self):
        return "<%s at %s, mode = %s>" % (self.__class__.__name__, id(self),
                                          str(self.mode))

    def to_abivars(self):
        if self:
            return {"ppmodel": self._mode2ppmodel[self.mode], "ppmfrq": self.plasmon_freq}
        else:
            return {}

    @classmethod
    def noppmodel(cls):
        return cls(mode="noppmodel", plasmon_freq=None)

    def as_dict(self):
        return {"mode": self.mode, "plasmon_freq": self.plasmon_freq,
                "@module": self.__class__.__module__,
                "@class": self.__class__.__name__}

    @staticmethod
    def from_dict(d):
        return PPModel(mode=d["mode"], plasmon_freq=d["plasmon_freq"])
Exemple #2
0
class KSampling(AbivarAble):
    """
    Input variables defining the K-point sampling.
    """
    # Modes supported by the constructor.
    modes = Enum(('monkhorst', 'path', 'automatic',))

    def __init__(self, mode="monkhorst", num_kpts= 0, kpts=((1, 1, 1),), kpt_shifts=(0.5, 0.5, 0.5),
                 kpts_weights=None, use_symmetries=True, use_time_reversal=True, chksymbreak=None,
                 comment=None):
        """
        Highly flexible constructor for KSampling objects.  The flexibility comes
        at the cost of usability and in general, it is recommended that you use
        the default constructor only if you know exactly what you are doing and
        requires the flexibility.  For most usage cases, the object be constructed
        far more easily using the convenience static constructors:

            #. gamma_only
            #. gamma_centered
            #. monkhorst
            #. monkhorst_automatic
            #. path

        and it is recommended that you use those.

        Args:
            mode: Mode for generating k-poits. Use one of the KSampling.modes enum types.
            num_kpts: Number of kpoints if mode is "automatic"
                Number of division for the sampling of the smallest segment if mode is "path".
                Not used for the other modes
            kpts: Number of divisions. Even when only a single specification is
                  required, e.g. in the automatic scheme, the kpts should still
                  be specified as a 2D array. e.g., [[20]] or [[2,2,2]].
            kpt_shifts: Shifts for Kpoints.
            use_symmetries: False if spatial symmetries should not be used
                to reduce the number of independent k-points.
            use_time_reversal: False if time-reversal symmetry should not be used
                to reduce the number of independent k-points.
            kpts_weights: Optional weights for kpoints. For explicit kpoints.
            chksymbreak: Abinit input variable: check whether the BZ sampling preserves the symmetry of the crystal.
            comment: String comment for Kpoints

        .. note::
            The default behavior of the constructor is monkhorst.
        """
        if mode not in KSampling.modes:
            raise ValueError("Unknown kpoint mode %s" % mode)

        super(KSampling, self).__init__()

        self.mode = mode
        self.comment = comment

        abivars = {}

        if mode in ("monkhorst",):
            assert num_kpts == 0
            ngkpt  = np.reshape(kpts, (-1,3))
            shiftk = np.reshape(kpt_shifts, (-1,3))

            if use_symmetries and use_time_reversal: kptopt = 1
            if not use_symmetries and use_time_reversal: kptopt = 2
            if not use_symmetries and not use_time_reversal: kptopt = 3
            if use_symmetries and not use_time_reversal: kptopt = 4

            abivars.update({
                "ngkpt"      : ngkpt,
                "shiftk"     : shiftk,
                "nshiftk"    : len(shiftk),
                "kptopt"     : kptopt,
                "chksymbreak": chksymbreak,
            })

        elif mode in ("path",):
            if num_kpts <= 0:
                raise ValueError("For Path mode, num_kpts must be specified and >0")

            kptbounds = np.reshape(kpts, (-1,3,))
            #print("in path with kptbound: %s " % kptbounds)

            abivars.update({
                "ndivsm"   : num_kpts,
                "kptbounds": kptbounds,
                "kptopt"   : -len(kptbounds)+1,
            })

        elif mode in ("automatic",):
            kpts = np.reshape(kpts, (-1,3))
            if len(kpts) != num_kpts:
                raise ValueError("For Automatic mode, num_kpts must be specified.")

            kptnrm = np.ones(num_kpts)

            abivars.update({
                "kptopt"     : 0,
                "kpt"        : kpts,
                "nkpt"       : num_kpts,
                "kptnrm"     : kptnrm,
                "wtk"        : kpts_weights,  # for iscf/=-2, wtk.
                "chksymbreak": chksymbreak,
            })

        else:
            raise ValueError("Unknown mode %s" % mode)

        self.abivars = abivars
        self.abivars["#comment"] = comment

    @property
    def is_homogeneous(self):
        return self.mode not in ["path"]

    @classmethod
    def gamma_only(cls):
        """Gamma-only sampling"""
        return cls(kpt_shifts=(0.0,0.0,0.0), comment="Gamma-only sampling")

    @classmethod
    def gamma_centered(cls, kpts=(1, 1, 1), use_symmetries=True, use_time_reversal=True):
        """
        Convenient static constructor for an automatic Gamma centered Kpoint grid.

        Args:
            kpts: Subdivisions N_1, N_2 and N_3 along reciprocal lattice vectors.
            use_symmetries: False if spatial symmetries should not be used
                to reduce the number of independent k-points.
            use_time_reversal: False if time-reversal symmetry should not be used
                to reduce the number of independent k-points.

        Returns:
            :class:`KSampling` object.
        """
        return cls(kpts=[kpts], kpt_shifts=(0.0, 0.0, 0.0),
                   use_symmetries=use_symmetries, use_time_reversal=use_time_reversal,
                   comment="gamma-centered mode")

    @classmethod
    def monkhorst(cls, ngkpt, shiftk=(0.5, 0.5, 0.5), chksymbreak=None, use_symmetries=True,
                  use_time_reversal=True, comment=None):
        """
        Convenient static constructor for a Monkhorst-Pack mesh.

        Args:
            ngkpt: Subdivisions N_1, N_2 and N_3 along reciprocal lattice vectors.
            shiftk: Shift to be applied to the kpoints.
            use_symmetries: Use spatial symmetries to reduce the number of k-points.
            use_time_reversal: Use time-reversal symmetry to reduce the number of k-points.

        Returns:
            :class:`KSampling` object.
        """
        return cls(
            kpts=[ngkpt], kpt_shifts=shiftk,
            use_symmetries=use_symmetries, use_time_reversal=use_time_reversal, chksymbreak=chksymbreak,
            comment=comment if comment else "Monkhorst-Pack scheme with user-specified shiftk")

    @classmethod
    def monkhorst_automatic(cls, structure, ngkpt,
                            use_symmetries=True, use_time_reversal=True, chksymbreak=None, comment=None):
        """
        Convenient static constructor for an automatic Monkhorst-Pack mesh.

        Args:
            structure: paymatgen structure object.
            ngkpt: Subdivisions N_1, N_2 and N_3 along reciprocal lattice vectors.
            use_symmetries: Use spatial symmetries to reduce the number of k-points.
            use_time_reversal: Use time-reversal symmetry to reduce the number of k-points.

        Returns:
            :class:`KSampling` object.
        """
        sg = SpacegroupAnalyzer(structure)
        #sg.get_crystal_system()
        #sg.get_point_group()
        # TODO
        nshiftk = 1
        #shiftk = 3*(0.5,) # this is the default
        shiftk = 3*(0.5,)

        #if lattice.ishexagonal:
        #elif lattice.isbcc
        #elif lattice.isfcc

        return cls.monkhorst(
            ngkpt, shiftk=shiftk, use_symmetries=use_symmetries, use_time_reversal=use_time_reversal,
            chksymbreak=chksymbreak, comment=comment if comment else "Automatic Monkhorst-Pack scheme")

    @classmethod
    def _path(cls, ndivsm, structure=None, kpath_bounds=None, comment=None):
        """
        Static constructor for path in k-space.

        Args:
            structure: pymatgen structure.
            kpath_bounds: List with the reduced coordinates of the k-points defining the path.
            ndivsm: Number of division for the smallest segment.
            comment: Comment string.

        Returns:
            :class:`KSampling` object.
        """
        if kpath_bounds is None:
            # Compute the boundaries from the input structure.
            from pymatgen.symmetry.bandstructure import HighSymmKpath
            sp = HighSymmKpath(structure)

            # Flat the array since "path" is a a list of lists!
            kpath_labels = []
            for labels in sp.kpath["path"]:
                kpath_labels.extend(labels)

            kpath_bounds = []
            for label in kpath_labels:
                red_coord = sp.kpath["kpoints"][label]
                #print("label %s, red_coord %s" % (label, red_coord))
                kpath_bounds.append(red_coord)

        return cls(mode=KSampling.modes.path, num_kpts=ndivsm, kpts=kpath_bounds,
                   comment=comment if comment else "K-Path scheme")

    @classmethod
    def path_from_structure(cls, ndivsm, structure):
        """See _path for the meaning of the variables"""
        return cls._path(ndivsm,  structure=structure, comment="K-path generated automatically from pymatgen structure")

    @classmethod
    def explicit_path(cls, ndivsm, kpath_bounds):
        """See _path for the meaning of the variables"""
        return cls._path(ndivsm, kpath_bounds=kpath_bounds, comment="Explicit K-path")

    @classmethod
    def automatic_density(cls, structure, kppa, chksymbreak=None, use_symmetries=True, use_time_reversal=True,
                          shifts=(0.5, 0.5, 0.5)):
        """
        Returns an automatic Kpoint object based on a structure and a kpoint
        density. Uses Gamma centered meshes for hexagonal cells and Monkhorst-Pack grids otherwise.

        Algorithm:
            Uses a simple approach scaling the number of divisions along each
            reciprocal lattice vector proportional to its length.

        Args:
            structure: Input structure
            kppa: Grid density
        """
        lattice = structure.lattice
        lengths = lattice.abc
        ngrid = kppa / structure.num_sites

        mult = (ngrid * lengths[0] * lengths[1] * lengths[2]) ** (1 / 3.)

        num_div = [int(round(1.0 / lengths[i] * mult)) for i in range(3)]
        # ensure that num_div[i] > 0
        num_div = [i if i > 0 else 1 for i in num_div]

        angles = lattice.angles
        hex_angle_tol = 5      # in degrees
        hex_length_tol = 0.01  # in angstroms

        right_angles = [i for i in range(3) if abs(angles[i] - 90) < hex_angle_tol]

        hex_angles = [i for i in range(3)
                      if abs(angles[i] - 60) < hex_angle_tol or
                      abs(angles[i] - 120) < hex_angle_tol]

        is_hexagonal = (len(right_angles) == 2 and len(hex_angles) == 1
                        and abs(lengths[right_angles[0]] -
                                lengths[right_angles[1]]) < hex_length_tol)

        #style = Kpoints.modes.gamma
        #if not is_hexagonal:
        #    num_div = [i + i % 2 for i in num_div]
        #    style = Kpoints.modes.monkhorst

        comment = "pymatgen generated KPOINTS with grid density = " + "{} / atom".format(kppa)

        shifts = np.reshape(shifts, (-1, 3))

        return cls(
            mode="monkhorst", num_kpts=0, kpts=[num_div], kpt_shifts=shifts,
            use_symmetries=use_symmetries, use_time_reversal=use_time_reversal, chksymbreak=chksymbreak,
            comment=comment)

    def to_abivars(self):
        return self.abivars