Example #1
0
        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")),
                ])
Example #2
0
        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)]),
            ])