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)
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)
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)
def test_wigner_seitz_cell(self): # test from structure rs = WignerSeitzCell.from_structure(self.structure) self.assert_rs_equal(rs, self.ref_rs_wigner)
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)