def _get_soap_graph(feature, label): spectrum = { "data": [{ "coloraxis": "coloraxis", #'hovertemplate': 'x: %{x}<br>y: %{y}<br>color: %{z}<extra></extra>', "type": "heatmap", "z": feature.tolist(), }] } spectrum["layout"] = { "xaxis": { "visible": False }, "yaxis": { "visible": False }, "paper_bgcolor": "rgba(0,0,0,0)", "plot_bgcolor": "rgba(0,0,0,0)", "coloraxis": { "colorscale": [ [0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"], ], "showscale": False, }, "margin": { "l": 0, "b": 0, "t": 0, "r": 0, "pad": 0 }, # "height": 20*feature.shape[0], # for fixed size plots # "width": 20*feature.shape[1] } return Columns([ Column(Label(label), size="1"), Column( dcc.Graph( figure=spectrum, config={"displayModeBar": False}, responsive=True, style={"height": "60px"}, )), ])
def update_contents(self, new_store_contents): struct = self.from_data(new_store_contents) msa = CollinearMagneticStructureAnalyzer(struct, round_magmoms=1) if not msa.is_magnetic: # TODO: detect magnetic elements (?) return html.Div( "This structure is not magnetic or does not have " "magnetic information associated with it." ) mag_species_and_magmoms = msa.magnetic_species_and_magmoms for k, v in mag_species_and_magmoms.items(): if not isinstance(v, list): mag_species_and_magmoms[k] = [v] magnetic_atoms = "\n".join( [ f"{sp} ({', '.join([f'{magmom} µB' for magmom in magmoms])})" for sp, magmoms in mag_species_and_magmoms.items() ] ) magnetization_per_formula_unit = ( msa.total_magmoms / msa.structure.composition.get_reduced_composition_and_factor()[1] ) rows = [] rows.append( ( html.B("Total magnetization per formula unit"), html.Br(), f"{magnetization_per_formula_unit:.1f} µB", ) ) rows.append((html.B("Atoms with local magnetic moments"), html.Br(), magnetic_atoms)) data_block = html.Div([html.P([html.Span(cell) for cell in row]) for row in rows]) viewer = StructureMoleculeComponent( struct, id=self.id("structure"), color_scheme="magmom", static=True ) return Columns([ Column(html.Div([viewer.struct_layout], style={"height": "60vmin"})), Column(data_block) ])
def layout(self): if self.dim in [2, 3]: return Columns([ Column([ html.Div(self._sub_layouts["energy"]), self._sub_layouts["allow_shallow"] ]), Column([self._sub_layouts["cpd"], self.vertex_list]) ]) else: return Columns([ Column([ self.vertex_list, self._sub_layouts["allow_shallow"], self._sub_layouts["cpd_label"] ]), Column(html.Div(self._sub_layouts["energy"])) ])
def update_contents(self, new_store_contents): #JSONViewComponent(id=self.id("json-editor"), # src=loads(new_store_contents)) return Columns( [ #Column(self.editor_layout), Column(self.json_layout), Column() ])
def _get_soap_graph(feature, label): spectrum = px.imshow(feature, aspect="equal", color_continuous_scale="plasma") coloraxis = spectrum.layout.coloraxis coloraxis["showscale"] = False layout = { "xaxis": { "visible": False }, "yaxis": { "visible": False }, "paper_bgcolor": "rgba(0,0,0,0)", "plot_bgcolor": "rgba(0,0,0,0)", "coloraxis": coloraxis, "margin": { "l": 0, "b": 0, "t": 0, "r": 0, "pad": 0 }, # "height": 20*feature.shape[0], # for fixed size plots # "width": 20*feature.shape[1] } spectrum.layout = layout return Columns([ Column(Label(label), size="1"), Column( dcc.Graph( figure=spectrum, config={"displayModeBar": False}, responsive=True, style={"height": "60px"}, )), ])
def initial_contents(self): # TODO: this is so dumb, PanelComponent needs a rethink # (this comment is definition of technical debt) editor = dcc.Textarea( id=self.id("editor"), rows=16, className="textarea", style={ "max-width": "88%", "max-height": "800px", "height": "100%" }, ) json = Box( dcc.SyntaxHighlighter(id=self.id("highlighted"), customStyle={ "height": "100%", "max-height": "800px" })) return Columns([Column(editor), Column(json)])
def update_contents(self, new_store_contents): editor = dcc.Textarea( id=self.id("editor"), rows=16, className="textarea", style={ "max-width": "88%", "max-height": "800px", "height": "100%" }, value=new_store_contents, ) json = Box( dcc.SyntaxHighlighter(id=self.id("highlighted"), customStyle={ "height": "100%", "max-height": "800px" }, children=new_store_contents)) return Columns([Column(editor), Column(json)])
def container_layout(self, state=None, structure=None) -> html.Div: """ :return: Layout defining transformation and its options. """ container = MessageContainer( [ MessageHeader( html.Div([ self._sub_layouts["enable"], html.Span( self.title, style={ "vertical-align": "middle", "margin-left": "1rem", }, ), ])), MessageBody([ Columns([ Column([ self._sub_layouts["description"], html.Br(), html.Div( self.options_layouts(state=state, structure=structure)), html.Br(), self._sub_layouts["message"], ]) ]) ]), ], kind="dark", id=self.id("container"), ) return container
def get_chemenv_analysis(struct, distance_cutoff, angle_cutoff): if not struct: raise PreventUpdate struct = self.from_data(struct) kwargs = self.reconstruct_kwargs_from_state( callback_context.inputs) distance_cutoff = kwargs["distance_cutoff"] angle_cutoff = kwargs["angle_cutoff"] # TODO: remove these brittle guard statements, figure out more robust way to handle multiple input types if isinstance(struct, StructureGraph): struct = struct.structure def get_valences(struct): valences = [ getattr(site.specie, "oxi_state", None) for site in struct ] valences = [v for v in valences if v is not None] if len(valences) == len(struct): return valences else: return "undefined" # decide which indices to present to user sga = SpacegroupAnalyzer(struct) symm_struct = sga.get_symmetrized_structure() inequivalent_indices = [ indices[0] for indices in symm_struct.equivalent_indices ] wyckoffs = symm_struct.wyckoff_symbols lgf = LocalGeometryFinder() lgf.setup_structure(structure=struct) se = lgf.compute_structure_environments( maximum_distance_factor=distance_cutoff + 0.01, only_indices=inequivalent_indices, valences=get_valences(struct), ) strategy = SimplestChemenvStrategy(distance_cutoff=distance_cutoff, angle_cutoff=angle_cutoff) lse = LightStructureEnvironments.from_structure_environments( strategy=strategy, structure_environments=se) all_ce = AllCoordinationGeometries() envs = [] unknown_sites = [] for index, wyckoff in zip(inequivalent_indices, wyckoffs): datalist = { "Site": unicodeify_species(struct[index].species_string), "Wyckoff Label": wyckoff, } if not lse.neighbors_sets[index]: unknown_sites.append( f"{struct[index].species_string} ({wyckoff})") continue # represent the local environment as a molecule mol = Molecule.from_sites( [struct[index]] + lse.neighbors_sets[index][0].neighb_sites) mol = mol.get_centered_molecule() mg = MoleculeGraph.with_empty_graph(molecule=mol) for i in range(1, len(mol)): mg.add_edge(0, i) view = html.Div( [ StructureMoleculeComponent( struct_or_mol=mg, disable_callbacks=True, id= f"{struct.composition.reduced_formula}_site_{index}", scene_settings={ "enableZoom": False, "defaultZoom": 0.6 }, )._sub_layouts["struct"] ], style={ "width": "300px", "height": "300px" }, ) env = lse.coordination_environments[index] co = all_ce.get_geometry_from_mp_symbol(env[0]["ce_symbol"]) name = co.name if co.alternative_names: name += f" (also known as {', '.join(co.alternative_names)})" datalist.update({ "Environment": name, "IUPAC Symbol": co.IUPAC_symbol_str, get_tooltip( "CSM", "The continuous symmetry measure (CSM) describes the similarity to an " "ideal coordination environment. It can be understood as a 'distance' to " "a shape and ranges from 0 to 100 in which 0 corresponds to a " "coordination environment that is exactly identical to the ideal one. A " "CSM larger than 5.0 already indicates a relatively strong distortion of " "the investigated coordination environment.", ): f"{env[0]['csm']:.2f}", "Interactive View": view, }) envs.append(get_data_list(datalist)) # TODO: switch to tiles? envs_grouped = [envs[i:i + 2] for i in range(0, len(envs), 2)] analysis_contents = [] for env_group in envs_grouped: analysis_contents.append( Columns([Column(e, size=6) for e in env_group])) if unknown_sites: unknown_sites = html.Strong( f"The following sites were not identified: {', '.join(unknown_sites)}. " f"Please try changing the distance or angle cut-offs to identify these sites, " f"or try an alternative algorithm such as LocalEnv.") else: unknown_sites = html.Span() return html.Div( [html.Div(analysis_contents), html.Br(), unknown_sites])
def update_contents(self, new_store_contents): return Columns([Column(self.editor_layout), Column(self.json_layout)])
def layout(self): return html.Div([Columns(Column(self._sub_layouts["graph"]))])
def layout(self): return Columns([ Column([self.vertex_list, self._sub_layouts["cpd_label"]]), Column(html.Div(self._sub_layouts["energy"])) ])
def retrieve_grain_boundaries(mpid): if not mpid or "mpid" not in mpid: raise PreventUpdate data = None with MPRester() as mpr: data = mpr.get_gb_data(mpid["mpid"]) if not data: return ( "No grain boundary information computed for this crystal structure. " "Grain boundary information has only been computed for elemental ground state " "crystal structures at present.") table_data = [{ "Sigma": d["sigma"], "Rotation Axis": f"{d['rotation_axis']}", "Rotation Angle / º": f"{d['rotation_angle']:.2f}", "Grain Boundary Plane": f"({' '.join(map(str, d['gb_plane']))})", "Grain Boundary Energy / Jm⁻²": f"{d['gb_energy']:.2f}", } for d in data] df = pd.DataFrame(table_data) table = dt.DataTable( id=self.id("table"), columns=[{ "name": i, "id": i } for i in df.columns], data=df.to_dict("records"), style_cell={ "minWidth": "0px", "maxWidth": "200px", "whiteSpace": "normal", }, css=[{ "selector": ".dash-cell div.dash-cell-value", "rule": "display: inline; white-space: inherit; overflow: inherit; text-overflow: inherit;", }], sort_action="native", sort_mode="multi", ) view = html.Div( [ StructureMoleculeComponent( data[2]["initial_structure"], id=self.id("struct"), static=True, color_scheme="grain_label", ).struct_layout ], style={ "width": "400px", "height": "400px" }, ) return Columns([Column(table), Column(view)])
def update_contents(data, symprec, angle_tolerance): if not data: return html.Div() struct = self.from_data(data) if not isinstance(struct, Structure): return html.Div( "Can only analyze symmetry of crystal structures at present." ) kwargs = self.reconstruct_kwargs_from_state( callback_context.inputs) symprec = kwargs["symprec"] angle_tolerance = kwargs["angle_tolerance"] if symprec <= 0: return html.Span( f"Please use a positive symmetry-finding tolerance (currently {symprec})." ) sga = SpacegroupAnalyzer(struct, symprec=symprec, angle_tolerance=angle_tolerance) try: data = dict() data["Crystal System"] = sga.get_crystal_system().title() data["Lattice System"] = sga.get_lattice_type().title() data["Hall Number"] = sga.get_hall() data["International Number"] = sga.get_space_group_number() data["Symbol"] = unicodeify_spacegroup( sga.get_space_group_symbol()) data["Point Group"] = unicodeify_spacegroup( sga.get_point_group_symbol()) sym_struct = sga.get_symmetrized_structure() except Exception: return html.Span( f"Failed to calculate symmetry with this combination of " f"symmetry-finding ({symprec}) and angle tolerances ({angle_tolerance})." ) datalist = get_data_list(data) wyckoff_contents = [] wyckoff_data = sorted( zip(sym_struct.wyckoff_symbols, sym_struct.equivalent_sites), key=lambda x: "".join(filter(lambda w: w.isalpha(), x[0])), ) for symbol, equiv_sites in wyckoff_data: wyckoff_contents.append( html.Label( f"{symbol}, {unicodeify_species(equiv_sites[0].species_string)}", className="mpc-label", )) site_data = [( self.pretty_frac_format(site.frac_coords[0]), self.pretty_frac_format(site.frac_coords[1]), self.pretty_frac_format(site.frac_coords[2]), ) for site in equiv_sites] wyckoff_contents.append(get_table(site_data)) return Columns([ Column([H5("Overview"), datalist]), Column([H5("Wyckoff Positions"), html.Div(wyckoff_contents)]), ])
CalcResults(structure=defect_structure, energy=0.5, **common)] de_common = dict(name="Va_O1", structure=defect_structure, site_symmetry="1", perturbed_structure=defect_structure, defect_center=[[0]*3]) defect_entries = [DefectEntry(charge=0, **de_common), DefectEntry(charge=1, **de_common)] corrections = [ManualCorrection(correction_energy=1.0), ManualCorrection(correction_energy=1.0)] cpd_e_component = CpdEnergyComponent(cpd_plot_info, perfect, defects, defect_entries, corrections, unitcell_vbm=0.0, unitcell_cbm=1.0) my_layout = html.Div([Column(cpd_e_component.layout)]) ctc.register_crystal_toolkit(app=app, layout=my_layout, cache=None) # In[5]: app.run_server(port=8099) #app.run_server(mode='inline', port=8096)
def get_chemenv_analysis(struct, distance_cutoff, angle_cutoff): if not struct: raise PreventUpdate struct = self.from_data(struct) distance_cutoff = float(distance_cutoff) angle_cutoff = float(angle_cutoff) # decide which indices to present to user sga = SpacegroupAnalyzer(struct) symm_struct = sga.get_symmetrized_structure() inequivalent_indices = [ indices[0] for indices in symm_struct.equivalent_indices ] wyckoffs = symm_struct.wyckoff_symbols lgf = LocalGeometryFinder() lgf.setup_structure(structure=struct) se = lgf.compute_structure_environments( maximum_distance_factor=distance_cutoff + 0.01, only_indices=inequivalent_indices, ) strategy = SimplestChemenvStrategy(distance_cutoff=distance_cutoff, angle_cutoff=angle_cutoff) lse = LightStructureEnvironments.from_structure_environments( strategy=strategy, structure_environments=se) all_ce = AllCoordinationGeometries() envs = [] unknown_sites = [] for index, wyckoff in zip(inequivalent_indices, wyckoffs): datalist = { "Site": struct[index].species_string, "Wyckoff Label": wyckoff, } if not lse.neighbors_sets[index]: unknown_sites.append( f"{struct[index].species_string} ({wyckoff})") continue # represent the local environment as a molecule mol = Molecule.from_sites( [struct[index]] + lse.neighbors_sets[index][0].neighb_sites) mol = mol.get_centered_molecule() mg = MoleculeGraph.with_empty_graph(molecule=mol) for i in range(1, len(mol)): mg.add_edge(0, i) view = html.Div( [ StructureMoleculeComponent( struct_or_mol=mg, static=True, id= f"{struct.composition.reduced_formula}_site_{index}", scene_settings={ "enableZoom": False, "defaultZoom": 0.6 }, ).all_layouts["struct"] ], style={ "width": "300px", "height": "300px" }, ) env = lse.coordination_environments[index] co = all_ce.get_geometry_from_mp_symbol(env[0]["ce_symbol"]) name = co.name if co.alternative_names: name += f" (also known as {', '.join(co.alternative_names)})" datalist.update({ "Environment": name, "IUPAC Symbol": co.IUPAC_symbol_str, get_tooltip( "CSM", '"Continuous Symmetry Measure," a measure of how symmetrical a ' "local environment is from most symmetrical at 0% to least " "symmetrical at 100%", ): f"{env[0]['csm']:.2f}%", "Interactive View": view, }) envs.append(get_data_list(datalist)) # TODO: switch to tiles? envs_grouped = [envs[i:i + 2] for i in range(0, len(envs), 2)] analysis_contents = [] for env_group in envs_grouped: analysis_contents.append( Columns([Column(e, size=6) for e in env_group])) if unknown_sites: unknown_sites = html.Strong( f"The following sites were not identified: {', '.join(unknown_sites)}. " f"Please try changing the distance or angle cut-offs to identify these sites." ) else: unknown_sites = html.Span() return html.Div( [html.Div(analysis_contents), html.Br(), unknown_sites])
def make_defect_formation_energy(args): formula = args.perfect_calc_results.structure.composition.reduced_formula chem_pot_diag = ChemPotDiag.from_yaml(args.cpd_yaml) pcr = args.perfect_calc_results defects, defect_entries, corrections, edge_states = [], [], [], [] for d in args.dirs: if args.skip_shallow: edge_states = BandEdgeStates.from_yaml(d / "band_edge_states.yaml") if edge_states.is_shallow: continue defects.append(loadfn(d / "calc_results.json")) defect_entries.append(loadfn(d / "defect_entry.json")) corrections.append(loadfn(d / "correction.json")) if args.web_gui: from crystal_toolkit.settings import SETTINGS import dash_html_components as html from crystal_toolkit.helpers.layouts import Column import crystal_toolkit.components as ctc import dash edge_states = [] for d in args.dirs: edge_states.append( BandEdgeStates.from_yaml(d / "band_edge_states.yaml")) app = dash.Dash(__name__, suppress_callback_exceptions=True, assets_folder=SETTINGS.ASSETS_PATH, external_stylesheets=[ 'https://codepen.io/chriddyp/pen/bWLwgP.css' ]) cpd_plot_info = CpdPlotInfo(chem_pot_diag) cpd_e_component = CpdEnergyComponent(cpd_plot_info, pcr, defects, defect_entries, corrections, args.unitcell.vbm, args.unitcell.cbm, edge_states) my_layout = html.Div([Column(cpd_e_component.layout)]) ctc.register_crystal_toolkit(app=app, layout=my_layout, cache=None) app.run_server(port=args.port) return abs_chem_pot = chem_pot_diag.abs_chem_pot_dict(args.label) title = " ".join([latexify(formula), "point", args.label]) defect_energies = make_energies(pcr, defects, defect_entries, corrections, abs_chem_pot) if args.print: defect_energies = slide_energy(defect_energies, args.unitcell.vbm) print(" charge E_f correction ") for e in defect_energies: print(e) print("") print("-- cross points -- ") for e in defect_energies: print(e.name) print( e.cross_points(ef_min=args.unitcell.vbm, ef_max=args.unitcell.cbm, base_ef=args.unitcell.vbm)) print("") return plotter = DefectEnergyMplPlotter(title=title, defect_energies=defect_energies, vbm=args.unitcell.vbm, cbm=args.unitcell.cbm, supercell_vbm=pcr.vbm, supercell_cbm=pcr.cbm, y_range=args.y_range, supercell_edge=args.supercell_edge, label_line=args.label_line, add_charges=args.add_charges) plotter.construct_plot() plotter.plt.savefig(f"energy_{args.label}.pdf")