def get_structure_graph_scene( self, origin=None, draw_image_atoms=True, bonded_sites_outside_unit_cell=True, hide_incomplete_edges=False, incomplete_edge_length_scale=0.3, color_edges_by_edge_weight=True, edge_weight_color_scale="coolwarm", explicitly_calculate_polyhedra_hull=False, legend: Optional[Legend] = None, group_by_symmetry: bool = True, ) -> Scene: origin = origin or list( -self.structure.lattice.get_cartesian_coords([0.5, 0.5, 0.5])) legend = legend or Legend(self.structure) primitives = defaultdict(list) sites_to_draw = self._get_sites_to_draw( draw_image_atoms=draw_image_atoms, bonded_sites_outside_unit_cell=bonded_sites_outside_unit_cell, ) color_edges = False if color_edges_by_edge_weight: weights = [e[2].get("weight") for e in self.graph.edges(data=True)] weights = np.array([w for w in weights if w]) if any(weights): cmap = get_cmap(edge_weight_color_scale) # try to keep color scheme symmetric around 0 weight_max = max([abs(min(weights)), max(weights)]) weight_min = -weight_max def get_weight_color(weight): if not weight: weight = 0 x = (weight - weight_min) / (weight_max - weight_min) return "#{:02x}{:02x}{:02x}".format( *[int(c * 255) for c in cmap(x)[0:3]]) color_edges = True idx_to_wyckoff = {} if group_by_symmetry: sga = SpacegroupAnalyzer(self.structure) struct_sym = sga.get_symmetrized_structure() for equiv_idxs, wyckoff in zip(struct_sym.equivalent_indices, struct_sym.wyckoff_symbols): for idx in equiv_idxs: idx_to_wyckoff[idx] = wyckoff for (idx, jimage) in sites_to_draw: site = self.structure[idx] if jimage != (0, 0, 0): connected_sites = self.get_connected_sites(idx, jimage=jimage) site = PeriodicSite( site.species, np.add(site.frac_coords, jimage), site.lattice, properties=site.properties, ) else: connected_sites = self.get_connected_sites(idx) connected_sites = [ cs for cs in connected_sites if (cs.index, cs.jimage) in sites_to_draw ] connected_sites_not_drawn = [ cs for cs in connected_sites if (cs.index, cs.jimage) not in sites_to_draw ] if color_edges: connected_sites_colors = [ get_weight_color(cs.weight) for cs in connected_sites ] connected_sites_not_drawn_colors = [ get_weight_color(cs.weight) for cs in connected_sites_not_drawn ] else: connected_sites_colors = None connected_sites_not_drawn_colors = None site_scene = site.get_scene( connected_sites=connected_sites, connected_sites_not_drawn=connected_sites_not_drawn, hide_incomplete_edges=hide_incomplete_edges, incomplete_edge_length_scale=incomplete_edge_length_scale, connected_sites_colors=connected_sites_colors, connected_sites_not_drawn_colors=connected_sites_not_drawn_colors, explicitly_calculate_polyhedra_hull= explicitly_calculate_polyhedra_hull, legend=legend, ) for scene in site_scene.contents: if group_by_symmetry and scene.name == "atoms" and idx in idx_to_wyckoff: # will rename to e.g. atoms_N_4e scene.name = f"atoms_{site_scene.name}_{idx_to_wyckoff[idx]}" # this is a proof-of-concept to demonstrate hover labels, could create label # automatically from site properties instead scene.contents[ 0].tooltip = f"{site_scene.name} ({idx_to_wyckoff[idx]})" primitives[scene.name] += scene.contents primitives["unit_cell"].append(self.structure.lattice.get_scene()) return Scene( name="StructureGraph", origin=origin, contents=[ Scene(name=k, contents=v, origin=origin) for k, v in primitives.items() ], )
def get_structure_graph_scene( self, origin=(0, 0, 0), draw_image_atoms=True, bonded_sites_outside_unit_cell=True, hide_incomplete_bonds=False, explicitly_calculate_polyhedra_hull=False, ) -> Scene: primitives = defaultdict(list) sites_to_draw = self._get_sites_to_draw( draw_image_atoms=draw_image_atoms, bonded_sites_outside_unit_cell=bonded_sites_outside_unit_cell, ) for (idx, jimage) in sites_to_draw: site = self.structure[idx] if jimage != (0, 0, 0): connected_sites = self.get_connected_sites(idx, jimage=jimage) site = PeriodicSite( site.species, np.add(site.frac_coords, jimage), site.lattice, properties=site.properties, ) else: connected_sites = self.get_connected_sites(idx) true_number_of_connected_sites = len(connected_sites) connected_sites_being_drawn = [ cs for cs in connected_sites if (cs.index, cs.jimage) in sites_to_draw ] number_of_connected_sites_drawn = len(connected_sites_being_drawn) all_connected_sites_present = ( true_number_of_connected_sites == number_of_connected_sites_drawn) if hide_incomplete_bonds: # only draw bonds if the destination site is also being drawn connected_sites = connected_sites_being_drawn site_scene = site.get_scene( connected_sites=connected_sites, all_connected_sites_present=all_connected_sites_present, origin=origin, explicitly_calculate_polyhedra_hull= explicitly_calculate_polyhedra_hull, ) for scene in site_scene.contents: primitives[scene.name] += scene.contents # we are here ... # select polyhedra # split by atom type at center # see if any intersect, if yes split further # order sets, with each choice, go to add second set etc if don't intersect # they intersect if centre atom forms vertex of another atom (caveat: centre atom may not actually be inside polyhedra! not checking for this, add todo) # def _set_intersects() ->bool: # def _split_set() ->List: (by type, then..?) # def _order_sets()... pick 1, ask can add 2? etc primitives["unit_cell"].append( self.structure.lattice.get_scene(origin=origin)) return Scene( name=self.structure.composition.reduced_formula, contents=[Scene(name=k, contents=v) for k, v in primitives.items()], )
def get_structure_graph_scene( self, origin=(0, 0, 0), draw_image_atoms=True, bonded_sites_outside_unit_cell=True, hide_incomplete_edges=False, incomplete_edge_length_scale=0.3, color_edges_by_edge_weight=True, edge_weight_color_scale="coolwarm", explicitly_calculate_polyhedra_hull=False, ) -> Scene: primitives = defaultdict(list) sites_to_draw = self._get_sites_to_draw( draw_image_atoms=draw_image_atoms, bonded_sites_outside_unit_cell=bonded_sites_outside_unit_cell, ) color_edges = False if color_edges_by_edge_weight: weights = [e[2].get("weight") for e in self.graph.edges(data=True)] weights = np.array([w for w in weights if w]) if any(weights): cmap = get_cmap(edge_weight_color_scale) # try to keep color scheme symmetric around 0 weight_max = max([abs(min(weights)), max(weights)]) weight_min = -weight_max def get_weight_color(weight): if not weight: weight = 0 x = (weight - weight_min) / (weight_max - weight_min) return "#{:02x}{:02x}{:02x}".format( *[int(c * 255) for c in cmap(x)[0:3]]) color_edges = True for (idx, jimage) in sites_to_draw: site = self.structure[idx] if jimage != (0, 0, 0): connected_sites = self.get_connected_sites(idx, jimage=jimage) site = PeriodicSite( site.species, np.add(site.frac_coords, jimage), site.lattice, properties=site.properties, ) else: connected_sites = self.get_connected_sites(idx) connected_sites = [ cs for cs in connected_sites if (cs.index, cs.jimage) in sites_to_draw ] connected_sites_not_drawn = [ cs for cs in connected_sites if (cs.index, cs.jimage) not in sites_to_draw ] if color_edges: connected_sites_colors = [ get_weight_color(cs.weight) for cs in connected_sites ] connected_sites_not_drawn_colors = [ get_weight_color(cs.weight) for cs in connected_sites_not_drawn ] else: connected_sites_colors = None connected_sites_not_drawn_colors = None site_scene = site.get_scene( connected_sites=connected_sites, connected_sites_not_drawn=connected_sites_not_drawn, hide_incomplete_edges=hide_incomplete_edges, incomplete_edge_length_scale=incomplete_edge_length_scale, connected_sites_colors=connected_sites_colors, connected_sites_not_drawn_colors=connected_sites_not_drawn_colors, origin=origin, explicitly_calculate_polyhedra_hull= explicitly_calculate_polyhedra_hull, ) for scene in site_scene.contents: primitives[scene.name] += scene.contents # we are here ... # select polyhedra # split by atom type at center # see if any intersect, if yes split further # order sets, with each choice, go to add second set etc if don't intersect # they intersect if centre atom forms vertex of another atom (caveat: centre atom may not actually be inside polyhedra! not checking for this, add todo) # def _set_intersects() ->bool: # def _split_set() ->List: (by type, then..?) # def _order_sets()... pick 1, ask can add 2? etc primitives["unit_cell"].append( self.structure.lattice.get_scene(origin=origin)) return Scene( name=self.structure.composition.reduced_formula, contents=[Scene(name=k, contents=v) for k, v in primitives.items()], )
def get_structure_graph_scene( self, origin=None, draw_image_atoms=True, bonded_sites_outside_unit_cell=True, hide_incomplete_edges=False, incomplete_edge_length_scale=0.3, color_edges_by_edge_weight=True, edge_weight_color_scale="coolwarm", explicitly_calculate_polyhedra_hull=False, legend: Optional[Legend] = None, ) -> Scene: origin = origin or list( -self.structure.lattice.get_cartesian_coords([0.5, 0.5, 0.5])) legend = legend or Legend(self.structure) primitives = defaultdict(list) sites_to_draw = self._get_sites_to_draw( draw_image_atoms=draw_image_atoms, bonded_sites_outside_unit_cell=bonded_sites_outside_unit_cell, ) color_edges = False if color_edges_by_edge_weight: weights = [e[2].get("weight") for e in self.graph.edges(data=True)] weights = np.array([w for w in weights if w]) if any(weights): cmap = get_cmap(edge_weight_color_scale) # try to keep color scheme symmetric around 0 weight_max = max([abs(min(weights)), max(weights)]) weight_min = -weight_max def get_weight_color(weight): if not weight: weight = 0 x = (weight - weight_min) / (weight_max - weight_min) return "#{:02x}{:02x}{:02x}".format( *[int(c * 255) for c in cmap(x)[0:3]]) color_edges = True for (idx, jimage) in sites_to_draw: site = self.structure[idx] if jimage != (0, 0, 0): connected_sites = self.get_connected_sites(idx, jimage=jimage) site = PeriodicSite( site.species, np.add(site.frac_coords, jimage), site.lattice, properties=site.properties, ) else: connected_sites = self.get_connected_sites(idx) connected_sites = [ cs for cs in connected_sites if (cs.index, cs.jimage) in sites_to_draw ] connected_sites_not_drawn = [ cs for cs in connected_sites if (cs.index, cs.jimage) not in sites_to_draw ] if color_edges: connected_sites_colors = [ get_weight_color(cs.weight) for cs in connected_sites ] connected_sites_not_drawn_colors = [ get_weight_color(cs.weight) for cs in connected_sites_not_drawn ] else: connected_sites_colors = None connected_sites_not_drawn_colors = None site_scene = site.get_scene( connected_sites=connected_sites, connected_sites_not_drawn=connected_sites_not_drawn, hide_incomplete_edges=hide_incomplete_edges, incomplete_edge_length_scale=incomplete_edge_length_scale, connected_sites_colors=connected_sites_colors, connected_sites_not_drawn_colors=connected_sites_not_drawn_colors, explicitly_calculate_polyhedra_hull= explicitly_calculate_polyhedra_hull, legend=legend, ) for scene in site_scene.contents: primitives[scene.name] += scene.contents primitives["unit_cell"].append(self.structure.lattice.get_scene()) return Scene( name=self.structure.composition.reduced_formula, origin=origin, contents=[ Scene(name=k, contents=v, origin=origin) for k, v in primitives.items() ], )