예제 #1
0
    def test_wigner_seitz_cell(self):
        # test from lattice
        rs = WignerSeitzCell(self.structure.lattice.reciprocal_lattice.matrix)
        self.assert_rs_equal(rs, self.ref_rs_wigner)

        # test from structure
        rs = WignerSeitzCell.from_structure(self.structure)
        self.assert_rs_equal(rs, self.ref_rs_wigner)
예제 #2
0
    def from_band_structure(
        cls,
        band_structure: BandStructure,
        kpoint_dim: np.ndarray,
        mu: float = 0.0,
        wigner_seitz: bool = False,
        symprec: float = 0.001,
        decimate_factor: Optional[float] = None,
        decimate_method: str = "quadric",
        smooth: bool = False,
    ) -> "FermiSurface":
        """
        Args:
            band_structure: A band structure. The k-points must cover the full
                Brillouin zone (i.e., not just be the irreducible mesh). Use
                the ``ifermi.interpolator.Interpolator`` class to expand the k-points to
                the full Brillouin zone if required.
            kpoint_dim: The dimension of the grid in reciprocal space on which the
                energy eigenvalues are defined.
            mu: Energy offset from the Fermi energy at which the iso-surface is
               shape of the resulting iso-surface.
            wigner_seitz: Controls whether the cell is the Wigner-Seitz cell
                or the reciprocal unit cell parallelepiped.
            symprec: Symmetry precision for determining whether the structure is the
                standard primitive unit cell.
            decimate_factor: If method is "quadric", factor is the scaling factor by
                which to reduce the number of faces. I.e., final # faces = initial
                # faces * factor. If method is "cluster", factor is the voxel size in
                which to cluster points. Default is None (no decimation).
            decimate_method: Algorithm to use for decimation. Options are "quadric"
                or "cluster".
            smooth: If True, will smooth resulting isosurface. Requires PyMCubes. See
                compute_isosurfaces for more information.
        """
        if np.product(kpoint_dim) != len(band_structure.kpoints):
            raise ValueError(
                "Number of k-points ({}) in band structure does not match number of "
                "k-points expected from mesh dimensions ({})".format(
                    len(band_structure.kpoints), np.product(kpoint_dim)
                )
            )

        band_structure = deepcopy(band_structure)  # prevent data getting overwritten

        structure = band_structure.structure
        fermi_level = band_structure.efermi + mu
        bands = band_structure.bands
        kpoints = np.array([k.frac_coords for k in band_structure.kpoints])

        # sort k-points to be in the correct order
        order = np.lexsort((kpoints[:, 2], kpoints[:, 1], kpoints[:, 0]))
        kpoints = kpoints[order]
        bands = {s: b[:, order] for s, b in bands.items()}

        if wigner_seitz:
            prim = get_prim_structure(structure, symprec=symprec)
            if not np.allclose(prim.lattice.matrix, structure.lattice.matrix, 1e-5):
                warnings.warn("Structure does not match expected primitive cell")

            reciprocal_space = WignerSeitzCell.from_structure(structure)
            bands, kpoints, kpoint_dim = _expand_bands(bands, kpoints, kpoint_dim)

        else:
            reciprocal_space = ReciprocalCell.from_structure(structure)

        kpoint_dim = tuple(kpoint_dim.astype(int))
        isosurfaces = compute_isosurfaces(
            bands, kpoint_dim, fermi_level, reciprocal_space,
            decimate_factor=decimate_factor, decimate_method=decimate_method,
            smooth=smooth
        )

        return cls(isosurfaces, reciprocal_space, structure)
예제 #3
0
    def from_band_structure(
        cls,
        band_structure: BandStructure,
        mu: float = 0.0,
        wigner_seitz: bool = False,
        decimate_factor: Optional[float] = None,
        decimate_method: str = "quadric",
        smooth: bool = False,
        property_data: Optional[Dict[Spin, np.ndarray]] = None,
        property_kpoints: Optional[np.ndarray] = None,
        calculate_dimensionality: bool = False,
    ) -> "FermiSurface":
        """
        Create a FermiSurface from a pymatgen band structure object.

        Args:
            band_structure: A band structure. The k-points must cover the full
                Brillouin zone (i.e., not just be the irreducible mesh). Use
                the ``ifermi.interpolator.FourierInterpolator`` class to expand the k-points to
                the full Brillouin zone if required.
            mu: Energy offset from the Fermi energy at which the isosurface is
                calculated.
            wigner_seitz: Controls whether the cell is the Wigner-Seitz cell
                or the reciprocal unit cell parallelepiped.
            decimate_factor: If method is "quadric", factor is the scaling factor by
                which to reduce the number of faces. I.e., final # faces = initial
                # faces * factor. If method is "cluster", factor is the voxel size in
                which to cluster points. Default is None (no decimation).
            decimate_method: Algorithm to use for decimation. Options are "quadric"
                or "cluster".
            smooth: If True, will smooth resulting isosurface. Requires PyMCubes. See
                compute_isosurfaces for more information.
            property_data: A property to project onto the Fermi surface. It should be
                given as a dict of ``{spin: properties}``, where properties is numpy
                array with shape (nbands, nkpoints, ...). The number of bands should
                equal the number of bands in the band structure but the k-point mesh
                can be different. Must be used in combination with
                ``kpoints``.
            property_kpoints: The k-points on which the data is generated.
                Must be used in combination with ``data``.
            calculate_dimensionality: Whether to calculate isosurface dimensionalities.

        Returns:
            A Fermi surface.
        """
        from ifermi.kpoints import get_kpoint_mesh_dim, kpoints_from_bandstructure

        band_structure = deepcopy(
            band_structure)  # prevent data getting overwritten

        structure = band_structure.structure
        fermi_level = band_structure.efermi + mu
        bands = band_structure.bands
        kpoints = kpoints_from_bandstructure(band_structure)

        kpoint_dim = get_kpoint_mesh_dim(kpoints)
        if np.product(kpoint_dim) != len(kpoints):
            raise ValueError(
                "Number of k-points ({}) in band structure does not match number of "
                "k-points expected from mesh dimensions ({})".format(
                    len(band_structure.kpoints), np.product(kpoint_dim)))

        if wigner_seitz:
            reciprocal_space = WignerSeitzCell.from_structure(structure)
        else:
            reciprocal_space = ReciprocalCell.from_structure(structure)

        interpolator = None
        if property_data is not None and property_kpoints is not None:
            interpolator = LinearInterpolator(property_kpoints, property_data)
        elif property_data is not None or property_kpoints is not None:
            raise ValueError("Both data and kpoints must be specified.")

        bands, kpoints = expand_bands(bands,
                                      kpoints,
                                      supercell_dim=(3, 3, 3),
                                      center=(1, 1, 1))
        isosurfaces = compute_isosurfaces(
            bands,
            kpoints,
            fermi_level,
            reciprocal_space,
            decimate_factor=decimate_factor,
            decimate_method=decimate_method,
            smooth=smooth,
            calculate_dimensionality=calculate_dimensionality,
            property_interpolator=interpolator,
        )

        return cls(isosurfaces, reciprocal_space, structure)
예제 #4
0
 def test_wigner_seitz_cell(self):
     # test from structure
     rs = WignerSeitzCell.from_structure(self.structure)
     self.assert_rs_equal(rs, self.ref_rs_wigner)
예제 #5
0
    def from_band_structure(
        cls,
        band_structure: BandStructure,
        kpoint_dim: np.ndarray,
        mu: float = 0.0,
        spin: Optional[Spin] = None,
        wigner_seitz: bool = False,
        symprec: float = 0.001,
    ) -> "FermiSurface":
        """
        Args:
            band_structure: A band structure. The k-points must cover the full
                Brillouin zone (i.e., not just be the irreducible mesh). Use
                the ``ifermi.interpolator.Interpolator`` class to expand the k-points to
                the full Brillouin zone if required.
            kpoint_dim: The dimension of the grid in reciprocal space on which the
                energy eigenvalues are defined.
            mu: Energy offset from the Fermi energy at which the iso-surface is
               shape of the resulting iso-surface.
            spin: The spin channel to plot. By default plots both spin channels.
            wigner_seitz: Controls whether the cell is the Wigner-Seitz cell
                or the reciprocal unit cell parallelepiped.
            symprec: Symmetry precision for determining whether the structure is the
                standard primitive unit cell.
        """
        if np.product(kpoint_dim) != len(band_structure.kpoints):
            raise ValueError(
                "Number of k-points ({}) in band structure does not match number of "
                "k-points expected from mesh dimensions ({})".format(
                    len(band_structure.kpoints), np.product(kpoint_dim)))

        band_structure = deepcopy(
            band_structure)  # prevent data getting overwritten

        structure = band_structure.structure
        fermi_level = band_structure.efermi + mu
        bands = band_structure.bands
        frac_kpoints = [k.frac_coords for k in band_structure.kpoints]
        frac_kpoints = np.array(frac_kpoints)

        if wigner_seitz:
            prim = get_prim_structure(structure, symprec=symprec)
            if not np.allclose(prim.lattice.matrix, structure.lattice.matrix,
                               1e-5):
                warnings.warn(
                    "Structure does not match expected primitive cell")

            reciprocal_space = WignerSeitzCell.from_structure(structure)
            bands, frac_kpoints, kpoint_dim = _expand_bands(
                bands, frac_kpoints, kpoint_dim)

        else:
            reciprocal_space = ReciprocalCell.from_structure(structure)

        kpoint_dim = tuple(kpoint_dim.astype(int))
        isosurfaces = compute_isosurfaces(
            bands,
            kpoint_dim,
            fermi_level,
            reciprocal_space,
            spin=spin,
        )

        return cls(isosurfaces, reciprocal_space, structure)