def run_algorithm(algorithm): algorithm = self.reconstruct_kwarg_from_state( callback_context.inputs, "algorithm") if algorithm == "chemenv": state = {"distance_cutoff": 1.4, "angle_cutoff": 0.3} description = ( "The ChemEnv algorithm is developed by David Waroquiers et al. to analyze " 'local chemical environments. In this interactive app, the "SimplestChemenvStrategy" ' 'and "LightStructureEnvironments" are used. For more powerful analysis, please use ' "the *pymatgen* code directly. Note that this analysis determines its own bonds independent " "of those shown in the main crystal visualizer.") distance_cutoff = self.get_numerical_input( label="Distance cut-off", kwarg_label="distance_cutoff", state=state, help_str= "Defines search radius by considering any atom within a radius " "of the minimum nearest neighbor distance multiplied by the distance " "cut-off.", shape=(), ) angle_cutoff = self.get_numerical_input( label="Angle cut-off", kwarg_label="angle_cutoff", state=state, help_str= "Defines a tolerance whereby a neighbor atom is excluded if the solid angle " "circumscribed by its Voronoi face is smaller than the angle tolerance " "multiplied by the largest solid angle present in the crystal.", shape=(), ) return html.Div([ dcc.Markdown(description), html.Br(), cite_me( cite_text="How to cite ChemEnv", doi="10.26434/chemrxiv.11294480.v1", ), html.Br(), distance_cutoff, angle_cutoff, html.Br(), Loading(id=self.id("chemenv_analysis")), ]) elif algorithm == "localenv": description = ( "The LocalEnv algorithm is developed by Nils Zimmerman et al. whereby " "an 'order parameter' is calculated that measures how well that " "environment matches an ideal polyhedra. The order parameter " "is a number from zero to one, with one being a perfect match." ) return html.Div([ dcc.Markdown(description), html.Br(), cite_me( cite_text="How to cite LocalEnv", doi="10.3389/fmats.2017.00034", ), html.Br(), Loading(id=self.id("localenv_analysis")), ]) elif algorithm == "bondinggraph": description = ( "This is an alternative way to display the same bonds present in the " "visualizer. Here, the bonding is displayed as a crystal graph, with " "nodes as atoms and edges as bonds. The graph visualization is shown in an " "abstract two-dimensional space.") return html.Div([ dcc.Markdown(description), html.Br(), Loading(id=self.id("bondinggraph_analysis")), ]) elif algorithm == "soap": state = { "rcut": 5.0, "nmax": 2, "lmax": 2, "sigma": 0.2, "crossover": True, "average": False, "rbf": "gto", "alpha": 0.1, "threshold": 1e-4, "metric": "linear", "normalize_kernel": True, } description = ( 'The "Smooth Overlap of Atomic Positions" (SOAP) descriptor provides information on the local ' "atomic environment by encoding that environment as a power spectrum derived from the " "spherical harmonics of atom-centered gaussian densities. The SOAP formalism is complex but is " "described well in [Bartók et al.](https://doi.org/10.1103/PhysRevB.87.184115) " "and the REMatch similarity kernel in [De et al.](https://doi.org/10.1039/c6cp00415f) " "The implementation of SOAP in this " "web app is provided by [DScribe](https://doi.org/10.1016/j.cpc.2019.106949). " "" "SOAP kernels are commonly used in machine learning applications. This interface is provided to " "help gain intuition and exploration of the behavior of SOAP kernels." ) rcut = self.get_numerical_input( label="Radial cut-off /Å", kwarg_label="rcut", state=state, help_str= "The radial cut-off that defines the local region being considered", shape=(), min=1.0001, ) nmax = self.get_numerical_input( label="N max.", kwarg_label="nmax", state=state, help_str="Number of radial basis functions", shape=(), is_int=True, min=1, max=9, ) lmax = self.get_numerical_input( label="L max.", kwarg_label="lmax", state=state, help_str="Maximum degree of spherical harmonics", shape=(), is_int=True, min=1, max=9, ) sigma = self.get_numerical_input( label="Sigma", kwarg_label="sigma", state=state, help_str= "The standard deviation of gaussians used to build atomic density", shape=(), min=0.00001, ) rbf = self.get_choice_input( label="Radial basis function", kwarg_label="rbf", state=state, help_str= "Polynomial basis is faster, spherical gaussian based was used in original formulation", options=[ { "label": "Spherical gaussian basis", "value": "gto" }, { "label": "Polynomial basis", "value": "polynomial" }, ], style={"width": "16rem"}, # TODO: remove in-line style ) crossover = self.get_bool_input( label="Crossover", kwarg_label="crossover", state=state, help_str= "If enabled, the power spectrum will include all combinations of elements present.", ) average = self.get_bool_input( label="Average", kwarg_label="average", state=state, help_str= "If enabled, the SOAP vector will be averaged across all sites.", ) alpha = self.get_numerical_input( label="Alpha", kwarg_label="alpha", state=state, help_str= "Determines the entropic penalty in the REMatch kernel. As alpha goes to infinity, the " "behavior of the REMatch kernel matches the behavior of the kernel where SOAP vectors " "are averaged across all sites. As alpha goes to zero, the kernel matches the best match " "kernel.", shape=(), min=0.00001, ) threshold = self.get_numerical_input( label="Sinkhorn threshold", kwarg_label="threshold", state=state, help_str= "Convergence threshold for the Sinkhorn algorithm. If values are too small, convergence " "may not be possible, and calculation time will increase.", shape=(), ) metric = self.get_choice_input( label="Metric", kwarg_label="metric", state=state, help_str= 'See scikit-learn\'s documentation on "Pairwise metrics, Affinities and Kernels" ' "for an explanation of available metrics.", options=[ # {"label": "Additive χ2", "value": "additive_chi2"}, # these seem to be unstable # {"label": "Exponential χ2", "value": "chi2"}, { "label": "Linear", "value": "linear" }, { "label": "Polynomial", "value": "polynomial" }, { "label": "Radial basis function", "value": "rbf" }, { "label": "Laplacian", "value": "laplacian" }, { "label": "Sigmoid", "value": "sigmoid" }, { "label": "Cosine", "value": "cosine" }, ], style={"width": "16rem"}, # TODO: remove in-line style ) normalize_kernel = self.get_bool_input( label="Normalize", kwarg_label="normalize_kernel", state=state, help_str= "Whether or not to normalize the resulting similarity kernel.", ) # metric_kwargs = self.get_dict_input() return html.Div([ dcc.Markdown(description), html.Br(), H5("SOAP parameters"), rcut, nmax, lmax, sigma, rbf, crossover, average, html.Br( ), # TODO: remove all html.Br(), add appropriate styles instead html.Br(), html.Div(id=self.id("soap_analysis")), html.Br(), html.Br(), H5("Similarity metric parameters"), html.Div( "This will calculate structural similarity scores from materials in the " "Materials Project in the same chemical system. Note that for large chemical " "systems this step can take several minutes."), html.Br(), alpha, threshold, metric, # normalize_kernel, html.Br(), html.Br(), Loading(id=self.id("soap_similarities")), ])
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)]), ])