def get_lattice_scene(self, origin=None, show_axes=False, **kwargs): o = -np.array((0, 0, 0)) a, b, c = self.matrix[0], self.matrix[1], self.matrix[2] line_pairs = [ o, o + a, o, o + b, o, o + c, o + a, o + a + b, o + a, o + a + c, o + b, o + b + a, o + b, o + b + c, o + c, o + c + a, o + c, o + c + b, o + a + b, o + a + b + c, o + a + c, o + a + b + c, o + b + c, o + a + b + c, ] line_pairs = [line.tolist() for line in line_pairs] name = ( f"a={self.a}, b={self.b}, c={self.c}, " f"alpha={self.alpha}, beta={self.beta}, gamma={self.gamma}" ) contents = [Lines(line_pairs, **kwargs)] if show_axes: contents.append(self._axes_from_lattice(origin=origin)) return Scene(name, contents, origin=origin)
def get_lattice_scene(self, origin=(0, 0, 0), **kwargs): o = -np.array(origin) a, b, c = self.matrix[0], self.matrix[1], self.matrix[2] line_pairs = [ o, o + a, o, o + b, o, o + c, o + a, o + a + b, o + a, o + a + c, o + b, o + b + a, o + b, o + b + c, o + c, o + c + a, o + c, o + c + b, o + a + b, o + a + b + c, o + a + c, o + a + b + c, o + b + c, o + a + b + c, ] line_pairs = [line.tolist() for line in line_pairs] name = (f"a={self.a}, b={self.b}, c={self.c}, " f"alpha={self.alpha}, beta={self.beta}, gamma={self.gamma}") return Scene(name, contents=[Lines(line_pairs, **kwargs)])
def plot_crystal_toolkit( self, spin: Optional[Spin] = None, colors: Optional[Union[str, dict, list]] = None, opacity: float = 1.0, ) -> "Scene": """ Get a crystal toolkit Scene showing the Fermi surface. The Scene can be displayed in an interactive web app using Crystal Toolkit, can be shown interactively in Jupyter Lab using the crystal-toolkit lab extension, or can be converted to JSON to store for future use. Args: spin: Which spin channel to plot. By default plot both spin channels if available. colors: See the docstring for ``get_isosurfaces_and_colors()`` for the available options. opacity: Opacity of surface. Note that due to limitations of WebGL, overlapping semi-transparent surfaces might result in visual artefacts. """ # The implementation here is very similar to the plotly implementation, except # the crystal toolkit scene is constructed using the scene primitives from # crystal toolkit (Spheres, Surface, Lines, etc.) scene_contents = [] isosurfaces, colors = self.get_isosurfaces_and_colors(spin=spin, colors=colors) if isinstance(colors, np.ndarray): colors = (colors * 255).astype(int) colors = ["rgb({},{},{})".format(*c) for c in colors] # create a mesh for each electron band which has an isosurfaces at the Fermi # energy mesh data is generated by a marching cubes algorithm when the # FermiSurface object is created. surfaces = [] for c, (verts, faces) in zip(colors, isosurfaces): positions = verts[faces].reshape(-1, 3).tolist() surface = Surface(positions=positions, color=c, opacity=opacity) surfaces.append(surface) fermi_surface = Scene("fermi_surface", contents=surfaces) scene_contents.append(fermi_surface) # add the cell outline to the plot lines = Lines(positions=list(self.reciprocal_space.lines.flatten())) # alternatively, # cylinders have finite width and are lighted, but no strong reason to choose # one over the other # cylinders = Cylinders(positionPairs=self.reciprocal_space.lines.tolist(), # radius=0.01, color="rgb(0,0,0)") scene_contents.append(lines) spheres = [] for position, label in zip(self._symmetry_pts[0], self._symmetry_pts[1]): sphere = Spheres( positions=[list(position)], tooltip=label, radius=0.05, color="rgb(0, 0, 0)", ) spheres.append(sphere) label_scene = Scene("labels", contents=spheres) scene_contents.append(label_scene) return Scene("ifermi", contents=scene_contents)
def get_brillouin_zone_scene(bs: BandStructureSymmLine) -> Scene: if not bs: return Scene(name="brillouin_zone", contents=[]) # TODO: from BSPlotter, merge back into BSPlotter # Brillouin zone bz_lattice = bs.structure.lattice.reciprocal_lattice bz = bz_lattice.get_wigner_seitz_cell() lines = [] for iface in range(len(bz)): # pylint: disable=C0200 for line in itertools.combinations(bz[iface], 2): for jface in range(len(bz)): if (iface < jface and any(np.all(line[0] == x) for x in bz[jface]) and any(np.all(line[1] == x) for x in bz[jface])): lines += [list(line[0]), list(line[1])] zone_lines = Lines(positions=lines) zone_surface = Convex(positions=lines, opacity=0.05, color="#000000") # - Strip latex math wrapping for labels # TODO: add to string utils in pymatgen str_replace = { "$": "", "\\mid": "|", "\\Gamma": "Γ", "\\Sigma": "Σ", "GAMMA": "Γ", "_1": "₁", "_2": "₂", "_3": "₃", "_4": "₄", "_{1}": "₁", "_{2}": "₂", "_{3}": "₃", "_{4}": "₄", "^{*}": "*", } labels = {} for k in bs.kpoints: if k.label: label = k.label for orig, new in str_replace.items(): label = label.replace(orig, new) labels[label] = bz_lattice.get_cartesian_coords(k.frac_coords) labels = [ Spheres(positions=[coords], tooltip=label, radius=0.03, color="#5EB1BF") for label, coords in labels.items() ] path = [] cylinder_pairs = [] for b in bs.branches: start = bz_lattice.get_cartesian_coords( bs.kpoints[b["start_index"]].frac_coords) end = bz_lattice.get_cartesian_coords( bs.kpoints[b["end_index"]].frac_coords) path += [start, end] cylinder_pairs += [[start, end]] # path_lines = Lines(positions=path, color="#ff4b5c",) path_lines = Cylinders(positionPairs=cylinder_pairs, color="#5EB1BF", radius=0.01) ibz_region = Convex(positions=path, opacity=0.2, color="#5EB1BF") contents = [zone_lines, zone_surface, path_lines, ibz_region, *labels] cbm = bs.get_cbm()["kpoint"] vbm = bs.get_vbm()["kpoint"] if cbm and vbm: if cbm.label: cbm_label = cbm.label for orig, new in str_replace.items(): cbm_label = cbm_label.replace(orig, new) cbm_label = f"CBM at {cbm_label}" else: cbm_label = "CBM" if cbm == vbm: cbm_label = f"VBM and {cbm_label}" cbm_coords = bz_lattice.get_cartesian_coords(cbm.frac_coords) cbm = Spheres(positions=[cbm_coords], tooltip=cbm_label, radius=0.05, color="#7E259B") contents.append(cbm) if cbm != vbm: if vbm.label: vbm_label = vbm.label for orig, new in str_replace.items(): vbm_label = vbm_label.replace(orig, new) vbm_label = f"VBM at {vbm_label}" else: vbm_label = "VBM" vbm_coords = bz_lattice.get_cartesian_coords(vbm.frac_coords) vbm = Spheres( positions=[vbm_coords], tooltip=vbm_label, radius=0.05, color="#7E259B", ) contents.append(vbm) return Scene(name="brillouin_zone", contents=contents)