diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 223baf16..e8467772 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -39,7 +39,6 @@ jobs: python${{ matrix.python-version }} -m pip install -r requirements/ubuntu-latest_py${{ matrix.python-version }}_extras.txt python${{ matrix.python-version }} -m pip install --upgrade pip python${{ matrix.python-version }} -m pip install --no-deps .[server,test] - python${{ matrix.python-version }} -m pip install lobsterpy - name: Test modules run: python${{ matrix.python-version }} -m pytest --cov=crystal_toolkit --cov-report=xml tests - name: Test example apps diff --git a/crystal_toolkit/_version.py b/crystal_toolkit/_version.py deleted file mode 100644 index 95755470..00000000 --- a/crystal_toolkit/_version.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = "2020.8.15.dev2217" \ No newline at end of file diff --git a/crystal_toolkit/apps/examples/cohp.py b/crystal_toolkit/apps/examples/cohp.py deleted file mode 100644 index 17f8c425..00000000 --- a/crystal_toolkit/apps/examples/cohp.py +++ /dev/null @@ -1,114 +0,0 @@ -import os -import warnings - -import dash -import numpy as np -from pymatgen.core import Structure -from pymatgen.electronic_structure.cohp import CompleteCohp -from pymatgen.io.lobster.inputs import Lobsterin -from pymatgen.io.lobster.outputs import ( - Bandoverlaps, - Charge, - Doscar, - Icohplist, - Lobsterout, - MadelungEnergies, -) -from pymatgen.io.vasp.outputs import Vasprun - -import crystal_toolkit.components as ctc -from crystal_toolkit.helpers.layouts import H3, Container -from crystal_toolkit.settings import SETTINGS - - -class CustomVasprun(Vasprun): - """Override final_energy property without unitized decorator""" - - def __init__(self, filename, **kwargs): - super().__init__(filename, **kwargs) - - @property - def final_energy(self) -> float: - """Final energy from the VASP run.""" - - try: - final_istep = self.ionic_steps[-1] - total_energy = final_istep["e_0_energy"] - - # Fix a bug in vasprun.xml. - # See https://www.vasp.at/forum/viewtopic.php?f=3&t=16942 - final_estep = final_istep["electronic_steps"][-1] - electronic_energy_diff = ( - final_estep["e_0_energy"] - final_estep["e_fr_energy"] - ) - total_energy_bugfix = np.round( - electronic_energy_diff + final_istep["e_fr_energy"], 8 - ) - if np.abs(total_energy - total_energy_bugfix) > 1e-7: - return total_energy_bugfix - - return total_energy - - except (IndexError, KeyError): - warnings.warn( - "Calculation does not have a total energy. Possibly a GW or similar kind of run. Infinity is returned.", - stacklevel=2, - ) - return float("inf") - - -calc_dir = "path/to/your/lobster/output" # Replace with your actual path - -icohplist_obj = Icohplist( - filename=f"{calc_dir}/ICOHPLIST.lobster.gz", are_cobis=False, are_coops=False -) - -completecohp_obj = CompleteCohp.from_file( - filename=f"{calc_dir}/COHPCAR.lobster.gz", - structure_file=f"{calc_dir}/CONTCAR.gz", - fmt="LOBSTER", - are_cobis=False, - are_coops=False, -) - -charge_obj = Charge(filename=f"{calc_dir}/CHARGE.lobster.gz") -madelung_obj = MadelungEnergies(filename=f"{calc_dir}/MadelungEnergies.lobster.gz") -lob_dos = Doscar( - doscar=f"{calc_dir}/DOSCAR.LSO.lobster.gz", structure_file=f"{calc_dir}/CONTCAR.gz" -) - -vasprun_obj = CustomVasprun(filename=f"{calc_dir}/vasprun.xml.gz") -structure_obj = Structure.from_file(f"{calc_dir}/CONTCAR.gz") -lobsterin_obj = Lobsterin.from_file(f"{calc_dir}/lobsterin.gz") -lobsterout_obj = Lobsterout(filename=f"{calc_dir}/lobsterout.gz") -# Include band overlaps file if it exists available -bandoverlaps_obj = ( - Bandoverlaps(filename=f"{calc_dir}/bandOverlaps.lobster.gz") - if os.path.exists(f"{calc_dir}/bandOverlaps.lobster.gz") - else None -) - -cohp_component = ctc.CohpAndDosComponent( - density_of_states=lob_dos.completedos, - charge_obj=charge_obj, - icohplist_obj=icohplist_obj, - completecohp_obj=completecohp_obj, - madelung_obj=madelung_obj, - vasprun_obj=vasprun_obj, - structure_obj=structure_obj, - lobsterin_obj=lobsterin_obj, - lobsterout_obj=lobsterout_obj, - bandoverlaps_obj=bandoverlaps_obj, - mpid="mp-xxx", - disable_callbacks=False, -) - -# example layout to demonstrate capabilities of component -layout = Container([H3("COHP and Density of States Example"), cohp_component.layout()]) - -app = dash.Dash(assets_folder=SETTINGS.ASSETS_PATH, prevent_initial_callbacks=True) # - -ctc.register_crystal_toolkit(app, layout=layout) - -if __name__ == "__main__": - app.run(debug=True, port=8051) diff --git a/crystal_toolkit/components/__init__.py b/crystal_toolkit/components/__init__.py index b6736a65..1b2cc6a6 100644 --- a/crystal_toolkit/components/__init__.py +++ b/crystal_toolkit/components/__init__.py @@ -4,11 +4,6 @@ BandstructureAndDosComponent, BandstructureAndDosPanelComponent, ) - -# from crystal_toolkit.components.transformations.rattle import ( -# MonteCarloRattleTransformationComponent, -# ) -from crystal_toolkit.components.cohp import CohpAndDosComponent from crystal_toolkit.components.diffraction import XRayDiffractionComponent from crystal_toolkit.components.diffraction_tem import TEMDiffractionComponent from crystal_toolkit.components.fermi_surface import FermiSurfaceComponent @@ -38,6 +33,10 @@ from crystal_toolkit.components.transformations.grainboundary import ( GrainBoundaryTransformationComponent, ) + +# from crystal_toolkit.components.transformations.rattle import ( +# MonteCarloRattleTransformationComponent, +# ) from crystal_toolkit.components.transformations.slab import SlabTransformationComponent from crystal_toolkit.components.transformations.substitution import ( SubstitutionTransformationComponent, diff --git a/crystal_toolkit/components/cohp.py b/crystal_toolkit/components/cohp.py deleted file mode 100644 index 38d05afa..00000000 --- a/crystal_toolkit/components/cohp.py +++ /dev/null @@ -1,781 +0,0 @@ -from __future__ import annotations - -import warnings -from importlib.metadata import version -from typing import TYPE_CHECKING - -from dash.dependencies import Component, Input, Output -from lobsterpy.cohp.analyze import Analysis -from lobsterpy.cohp.describe import Description -from plotly.subplots import make_subplots -from pymatgen.analysis.graphs import MoleculeGraph -from pymatgen.core import Molecule, Structure -from pymatgen.electronic_structure.cohp import CompleteCohp -from pymatgen.electronic_structure.dos import LobsterCompleteDos -from pymatgen.io.lobster.inputs import Lobsterin -from pymatgen.io.lobster.outputs import ( - Bandoverlaps, - Charge, - Icohplist, - Lobsterout, - MadelungEnergies, -) -from pymatgen.util.string import unicodeify_species - -from crystal_toolkit.components.bandstructure import BandstructureAndDosComponent -from crystal_toolkit.components.structure import StructureMoleculeComponent -from crystal_toolkit.core.mpcomponent import MPComponent -from crystal_toolkit.core.panelcomponent import PanelComponent -from crystal_toolkit.helpers.layouts import ( - H4, - Column, - Columns, - MessageBody, - MessageContainer, - dcc, - get_table, - html, -) - -if TYPE_CHECKING: - import plotly.graph_objects as go - from pymatgen.io.vasp.outputs import Vasprun - -warnings.filterwarnings("ignore") - - -class CohpAndDosComponent(MPComponent): - def __init__( - self, - charge_obj: Charge | None = None, - completecohp_obj: CompleteCohp | None = None, - icohplist_obj: Icohplist | None = None, - madelung_obj: MadelungEnergies | None = None, - mpid: str | None = None, - density_of_states: LobsterCompleteDos | None = None, - lobsterin_obj: Lobsterin | None = None, - lobsterout_obj: Lobsterout | None = None, - bandoverlaps_obj: Bandoverlaps | None = None, - vasprun_obj: Vasprun | None = None, - structure_obj: Structure | None = None, - id: str | None = None, - **kwargs, - ) -> None: - super().__init__( - id=id, - default_data={ - "charge_obj": charge_obj, - "completecohp_obj": completecohp_obj, - "icohplist_obj": icohplist_obj, - "madelung_obj": madelung_obj, - "mpid": mpid, - "density_of_states": density_of_states, - "lobsterin_obj": lobsterin_obj, - "lobsterout_obj": lobsterout_obj, - "bandoverlaps_obj": bandoverlaps_obj, - "vasprun_obj": vasprun_obj, - "structure_obj": structure_obj, - }, - **kwargs, - ) - - @property - def _sub_layouts(self) -> dict[str, Component]: - completecohp_obj, charge_obj, icohplist_obj, madelung_obj, dos = ( - CohpAndDosComponent._get_plot_inputs(self.initial_data["default"]) - ) - - fig = CohpAndDosComponent.get_figure( - charge_obj=charge_obj, - completecohp_obj=completecohp_obj, - madelung_obj=madelung_obj, - icohplist_obj=icohplist_obj, - dos=dos, - ) - - # Main plot - graph = html.Div( - [ - dcc.Graph( - figure=fig, - config={"displayModeBar": False}, - responsive=True, - style={"width": "100%"}, - ) - ], - id=self.id("cohp-dos-graph"), - ) - - analysis_options = [ - {"label": "all", "value": "all"}, - {"label": "cation-anion", "value": "cation-anion"}, - ] - - state = {"analysis-mode": "all"} - - analysis_mode = html.Div( - [ - self.get_choice_input( - kwarg_label="analysis-mode", - state=state, - label="LobsterPy analysis mode", - help_str="Analysis mode to choose from", - options=analysis_options, - ) - ], - style={"width": "200px"}, - id=self.id("options-container"), - ) - - analysis_description = CohpAndDosComponent.get_summary_text( - charge_obj=charge_obj, - completecohp_obj=completecohp_obj, - icohplist_obj=icohplist_obj, - dos=dos, - madelung_obj=madelung_obj, - which_bonds="all", - ) - - calc_quality_description = self.get_calc_quality_text( - input_dict=self._get_all_inputs(self.initial_data["default"]) - ) - - lobsterpy_version = version("lobsterpy") - - repo_link = html.A( - f"LobsterPy v{lobsterpy_version}", - href="https://github.com/JaGeo/LobsterPy.git", - style={"white-space": "nowrap"}, - ) - - analysis_description_div = html.Div( - [ - MessageContainer( - MessageBody( - [f"{analysis_description} - ", repo_link], - ), - kind="dark", - size="normal", - ), - ], - id=self.id("analysis-description"), - # style={"position": "relative"} - ) - - calc_quality_description_div = html.Div( - [ - MessageContainer( - MessageBody( - [f"{calc_quality_description}"], - ), - kind="info", - size="normal", - ) - ], - id=self.id("calc-quality-description"), - # style={"position": "relative"} - ) - - # LobsterPy local environments - local_envs = html.Div( - children=[ - CohpAndDosComponent.get_lobster_local_envs( - charge_obj=charge_obj, - completecohp_obj=completecohp_obj, - icohplist_obj=icohplist_obj, - madelung_obj=madelung_obj, - which_bonds="all", - ) - ], - id=self.id("local-env-lobsterpy"), - ) - - return { - "graph": graph, - "analysis-mode": analysis_mode, - "analysis-description": analysis_description_div, - "calc-quality-description": calc_quality_description_div, - "local-envs": local_envs, - } - - def layout(self): - """Return the layout of the component.""" - # Get the sub-layouts - # and create the main layout - sub_layouts = self._sub_layouts - - graph = sub_layouts["graph"] - - controls = Columns( - [ - Column( - [ - sub_layouts["analysis-mode"], - ] - ) - ] - ) - - # Create the description div - description_header = H4( - "Bonding analysis summary", - id=self.id("summary_text"), - style={"display": "inline-block"}, - ) - - description_div = Columns([Column([sub_layouts["analysis-description"]])]) - - calc_quality_header = H4( - "Calculation quality", - id=self.id("calc-quality-text"), - style={"display": "inline-block"}, - ) - calc_quality_div = Columns([Column([sub_layouts["calc-quality-description"]])]) - - # Create the local environments div - local_envs_header = H4( - "Local Environments identified via LobsterEnv", - id=self.id("local-envs-text"), - style={"display": "inline-block"}, - ) - local_envs_div = Columns([Column([sub_layouts["local-envs"]])]) - - return Column( - [ - controls, - graph, - html.Br(), - description_header, - description_div, - calc_quality_header, - calc_quality_div, - local_envs_header, - local_envs_div, - ] - ) - - @staticmethod - def _get_plot_inputs( - data: dict | None, - ) -> ( - tuple[CompleteCohp, Charge, Icohplist, MadelungEnergies, LobsterCompleteDos] - | tuple[None, None, None, None, None] - ): - data = data or {} - - charge_obj = data.get("charge_obj") - completecohp_obj = data.get("completecohp_obj") - icohplist_obj = data.get("icohplist_obj") - dos_obj = data.get("density_of_states") - madelung_obj = data.get("madelung_obj") - - if charge_obj and isinstance(charge_obj, dict): - charge_obj = Charge.from_dict(charge_obj) - - if completecohp_obj and isinstance(completecohp_obj, dict): - completecohp_obj = CompleteCohp.from_dict(completecohp_obj) - - if icohplist_obj and isinstance(icohplist_obj, dict): - icohplist_obj = Icohplist.from_dict(icohplist_obj) - - if dos_obj and isinstance(dos_obj, dict): - dos_obj = LobsterCompleteDos.from_dict(dos_obj) - - if madelung_obj and isinstance(madelung_obj, dict): - madelung_obj = MadelungEnergies.from_dict(madelung_obj) - - return completecohp_obj, charge_obj, icohplist_obj, madelung_obj, dos_obj - - @staticmethod - def _get_all_inputs( - data: dict | None, - ) -> dict: - data = data or {} - - charge_obj = data.get("charge_obj") - completecohp_obj = data.get("completecohp_obj") - icohplist_obj = data.get("icohplist_obj") - lob_dos_obj = data.get("density_of_states") - madelung_obj = data.get("madelung_obj") - lobsterin_obj = data.get("lobsterin_obj") - lobsterout_obj = data.get("lobsterout_obj") - bandoverlaps_obj = data.get("bandoverlaps_obj") - structure_obj = data.get("structure_obj") - - if charge_obj and isinstance(charge_obj, dict): - data["charge_obj"] = Charge.from_dict(charge_obj) - - if completecohp_obj and isinstance(completecohp_obj, dict): - data["completecohp_obj"] = CompleteCohp.from_dict(completecohp_obj) - - if icohplist_obj and isinstance(icohplist_obj, dict): - data["icohplist_obj"] = Icohplist.from_dict(icohplist_obj) - - if lob_dos_obj and isinstance(lob_dos_obj, dict): - data["density_of_states"] = LobsterCompleteDos.from_dict(lob_dos_obj) - - if madelung_obj and isinstance(madelung_obj, dict): - data["madelung_obj"] = MadelungEnergies.from_dict(madelung_obj) - - if lobsterin_obj and isinstance(lobsterin_obj, dict): - data["lobsterin_obj"] = Lobsterin.from_dict(lobsterin_obj) - - if lobsterout_obj and isinstance(lobsterout_obj, dict): - data["lobsterout_obj"] = Lobsterout.from_dict(lobsterout_obj) - - if bandoverlaps_obj and isinstance(bandoverlaps_obj, dict): - data["bandoverlaps_obj"] = Bandoverlaps.from_dict(bandoverlaps_obj) - - if structure_obj and isinstance(structure_obj, dict): - data["structure_obj"] = Structure.from_dict(structure_obj) - - return data - - @staticmethod - def get_calc_quality_text( - input_dict: dict, - ) -> str: - """Get text description of calculation quality - - Args: - input_dict: Dictionary containing the pymatgen objects. - - Returns: - A string describing the calculation quality. - """ - - calc_quality_dict = Analysis.get_lobster_calc_quality_summary( - charge_obj=input_dict.get("charge_obj"), - lobster_completedos_obj=input_dict.get("density_of_states"), - vasprun_obj=input_dict.get("vasprun_obj"), - lobsterin_obj=input_dict.get("lobsterin_obj"), - lobsterout_obj=input_dict.get("lobsterout_obj"), - bandoverlaps_obj=input_dict.get("bandoverlaps_obj"), - structure_obj=input_dict.get("structure_obj"), - e_range=[-15, 0], - dos_comparison=True, - n_bins=256, - bva_comp=True, - ) - calc_quality_description = Description.get_calc_quality_description( - calc_quality_dict - ) - - return " ".join(calc_quality_description) - - @staticmethod - def get_lobster_local_envs( - charge_obj, completecohp_obj, icohplist_obj, madelung_obj, which_bonds="all" - ) -> str: - """Get text description of local environments - - Args: - input_dict: Dictionary containing the pymatgen objects. - - Returns: - A string describing the local environments. - """ - # Get the local environments using LobsterPy - analyse = Analysis( - charge_obj=charge_obj, - madelung_obj=madelung_obj, - icohplist_obj=icohplist_obj, - completecohp_obj=completecohp_obj, - path_to_poscar=None, - path_to_icohplist=None, - path_to_cohpcar=None, - which_bonds=which_bonds, - summed_spins=False, - ) - - envs = [] # list of local environments - for site_ix, env in enumerate(analyse.lse.coordination_environments): - if site_ix in analyse.seq_ineq_ions and env[0]["ce_symbol"]: - # if env[0]["ce_symbol"]: - data_list = [] - site_str = unicodeify_species(analyse.structure[site_ix].species_string) - - try: - data_list.extend( - [ - ["Site", site_str], - [ - "Environment", - Description._coordination_environment_to_text( - env[0]["ce_symbol"] - ).capitalize(), - ], - ["IUPAC Symbol", env[0]["ce_symbol"]], - ["CSM", float(round(env[0]["csm"], 5))], - ] - ) - - except KeyError: - data_list.extend( - [ - ["Site", site_str], - [ - "Environment", - Description._coordination_environment_to_text( - env[0]["ce_symbol"] - ).capitalize(), - ], - ["IUPAC Symbol", env[0]["ce_symbol"]], - ["CSM", "NA"], - ] - ) - - local_env_data = analyse.chemenv.get_nn_info(analyse.structure, site_ix) - - neighbour_sites = [i["site"] for i in local_env_data] - central_site = analyse.structure[site_ix] - neighbour_weights = [ - i["edge_properties"]["ICOHP"] for i in local_env_data - ] - charges = [analyse.charge_obj.mulliken[site_ix]] - charges.extend( - [ - analyse.charge_obj.mulliken[i["site_index"]] - for i in local_env_data - ] - ) - - # Create a molecule object for the local environment - # and add the charges as a site property - mol = Molecule.from_sites([central_site, *neighbour_sites]) - mol = mol.get_centered_molecule() - - # Add the charges as a site property (hover text) - mol = mol.add_site_property("charge", charges) - - mg = MoleculeGraph.with_empty_graph( - molecule=mol, - name="bond_strength", - edge_weight_name="ICOHP", - edge_weight_units="eV", - ) - for i in range(1, len(mol)): - # Add the bond strength as an edge weight (hover text) - mg.add_edge(0, i, weight=neighbour_weights[i - 1]) - - view = html.Div( - [ - StructureMoleculeComponent( - struct_or_mol=mg, - disable_callbacks=True, - id=f"{analyse.structure.composition.reduced_formula}_site_{site_ix}", - scene_settings={ - "enableZoom": False, - "defaultZoom": 0.6, - }, - )._sub_layouts["struct"] - ], - style={"width": "300px", "height": "300px"}, - ) - - data_list.append(["Interactive", view]) - - envs.append(get_table(rows=data_list)) - - envs_grouped = [envs[i : i + 2] for i in range(0, len(envs), 2)] - # analysis_contents = [ - # Columns([Column(e) for e in env_group]) for env_group in envs_grouped - # ] - analysis_contents = [ - Columns( - [ - Column( - html.Div( - e, style={"display": "flex", "justifyContent": "center"} - ), - ) - for e in env_group - ] - ) - for env_group in envs_grouped - ] - - return html.Div([html.Div(analysis_contents), html.Br()]) - - @staticmethod - def get_summary_text( - charge_obj, - completecohp_obj, - icohplist_obj, - dos, - madelung_obj, - which_bonds="all", - ) -> str: - """Get text description of bonding analysis and calculation quality - - Args: - charge_obj: pymatgen lobster.io.charge object. - completecohp_obj: pymatgen.electronic_structure.cohp.CompleteCohp object - icohplist_obj: pymatgen lobster.io.Icohplist object - madelung_obj: pymatgen lobster.io.MadelungEnergies object - which_bonds: Bonds to consider for the analysis. - dos: pymatgen.electronic_structure.dos.LobsterCompleteDos object - kwargs: Keyword arguments that get passed to InteractiveCohpPlotter.get_plot. - Returns: - A string describing the bonding analysis. - """ - - analyse = Analysis( - charge_obj=charge_obj, - madelung_obj=madelung_obj, - icohplist_obj=icohplist_obj, - completecohp_obj=completecohp_obj, - path_to_poscar=None, - path_to_icohplist=None, - path_to_cohpcar=None, - which_bonds=which_bonds, - summed_spins=False, - ) - - description = Description(analysis_object=analyse) - - return " ".join(description.text) - - # return anaylsis_des - - @staticmethod - def get_figure( - charge_obj, - completecohp_obj, - icohplist_obj, - dos, - madelung_obj, - dos_select="ap", - energy_window=(-10.0, 5.0), - which_bonds="all", - **kwargs, - ) -> go.Figure: - """Get a COHP figure. - - Args: - charge_obj: pymatgen lobster.io.charge object. - completecohp_obj: pymatgen.electronic_structure.cohp.CompleteCohp object - icohplist_obj: pymatgen lobster.io.Icohplist object - madelung_obj: pymatgen lobster.io.MadelungEnergies object - kwargs: Keyword arguments that get passed to InteractiveCohpPlotter.get_plot. - - Returns: - A plotly Figure object. - """ - - analyse = Analysis( - charge_obj=charge_obj, - madelung_obj=madelung_obj, - icohplist_obj=icohplist_obj, - completecohp_obj=completecohp_obj, - path_to_poscar=None, - path_to_icohplist=None, - path_to_cohpcar=None, - which_bonds=which_bonds, - summed_spins=False, - ) - - description = Description(analysis_object=analyse) - - # Get the COHP plot - cohp_fig = description.plot_interactive_cohps( - ylim=[-10, 5], xlim=[-5, 5], hide=True - ) - - dos_traces = BandstructureAndDosComponent.get_dos_traces( - dos=dos, energy_window=energy_window, dos_select=dos_select - ) - - fig = make_subplots( - rows=1, - cols=2, - shared_yaxes=True, - horizontal_spacing=0.05, - column_widths=[0.6, 0.4], - ) - - # Adapt traces names and formatting to crystal-toolkit style - for ix, trace in enumerate(cohp_fig.data): - trace.visible = True - trace.line.width = None - if trace.line.dash: - trace.line.dash = "dot" - if trace.line.dash != "dot": - trace.name = f"{cohp_fig.data[ix].name} (spin ↑)" - legend_spin_down_name = trace.name.split(" (") - cohp_fig.data[ix + 1].name = f"{legend_spin_down_name[0]} (spin ↓)" - - fig.add_trace(trace, row=1, col=1) - - for trace in dos_traces: - fig.add_trace(trace, row=1, col=2) - - # Update axes layout to match Crystal Toolkit's aesthetic - fig.update_layout( - xaxis1=dict( - title="COHP (eV)", - range=[-5, 5], - showgrid=False, - linecolor="rgb(71,71,71)", - mirror=True, - domain=[0, 0.62], # 60% width for COHP - ), - xaxis2=dict( - title="DOS", - showgrid=False, - linecolor="rgb(71,71,71)", - mirror=True, - domain=[0.65, 1], # 35% width for DOS - ), - yaxis=dict( - title="E - EF (eV)", - range=[-10, 5], - showgrid=False, - linecolor="rgb(71,71,71)", - mirror=True, - ), - yaxis2=dict( - range=[-10, 5], showgrid=False, linecolor="rgb(71,71,71)", mirror=True - ), - hovermode="closest", - plot_bgcolor="rgba(230,230,230,230)", - margin=dict(l=60, b=50, t=50, pad=0, r=30), - # height=500, - # width=1000, - autosize=True, - showlegend=True, - ) - - legend = dict( - x=1.02, - y=1.005, - xanchor="left", - yanchor="top", - bordercolor="#333", - borderwidth=2, - traceorder="normal", - ) - - fig["layout"]["legend"] = legend - - return fig - - def generate_callbacks(self, app, cache) -> None: - """Register callback functions for this component.""" - - @app.callback( - Output(self.id("cohp-dos-graph"), "children"), - Input(self.id(), "data"), - Input(self.get_kwarg_id("analysis-mode"), "value"), - ) - def update_graph(data, label_select): - """Update the COHP and DOS graph.""" - - # Get the data from the store - completecohp_obj, charge_obj, icohplist_obj, madelung_obj, dos = ( - self._get_plot_inputs(data) - ) - - fig = self.get_figure( - charge_obj=charge_obj, - completecohp_obj=completecohp_obj, - madelung_obj=madelung_obj, - icohplist_obj=icohplist_obj, - dos=dos, - which_bonds=label_select - if isinstance(label_select, str) - else label_select[0], - ) - - return dcc.Graph( - figure=fig, - config={"displayModeBar": False}, - style={"width": "100%"}, - ) - - @app.callback( - Output(self.id("analysis-description"), "children"), - Input(self.id(), "data"), - Input(self.get_kwarg_id("analysis-mode"), "value"), - ) - def update_text(data, label_select) -> MessageContainer: - """Update the text description of the bonding analysis.""" - completecohp_obj, charge_obj, icohplist_obj, madelung_obj, dos = ( - self._get_plot_inputs(data) - ) - - analysis_description = self.get_summary_text( - charge_obj=charge_obj, - completecohp_obj=completecohp_obj, - icohplist_obj=icohplist_obj, - dos=dos, - madelung_obj=madelung_obj, - which_bonds=label_select - if isinstance(label_select, str) - else label_select[0], - ) - - lobsterpy_version = version("lobsterpy") - - repo_link = html.A( - f"LobsterPy v{lobsterpy_version}", - href="https://github.com/JaGeo/LobsterPy.git", - style={"white-space": "nowrap"}, - ) - - return MessageContainer( - MessageBody([f"{analysis_description} - ", repo_link]), - kind="dark", - size="normal", - ) - - @app.callback( - Output(self.id("local-env-lobsterpy"), "children"), - Input(self.id(), "data"), - Input(self.get_kwarg_id("analysis-mode"), "value"), - ) - def update_local_envs(data, label_select): - """Update the local environments using LobsterEnv.""" - completecohp_obj, charge_obj, icohplist_obj, madelung_obj, _ = ( - self._get_plot_inputs(data) - ) - - return self.get_lobster_local_envs( - charge_obj=charge_obj, - completecohp_obj=completecohp_obj, - icohplist_obj=icohplist_obj, - madelung_obj=madelung_obj, - which_bonds=label_select - if isinstance(label_select, str) - else label_select[0], - ) - - # return local_envs - - -class COHPAndDosPanelComponent(PanelComponent): - def __init__(self, *args, **kwargs) -> None: - super().__init__(*args, **kwargs) - self.cohp = CohpAndDosComponent() - self.cohp.attach_from(self, this_store_name="mpid") - - @property - def title(self) -> str: - return "COHP and Density of States" - - @property - def description(self) -> str: - return "Display the COHP and density of states for this structure \ - if it has been calculated by the Materials Project." - - @property - def initial_contents(self) -> html.Div: - return html.Div( - [ - super().initial_contents, - html.Div([self.cohp.standard_layout], style={"display": "none"}), - ] - ) - - def update_contents(self, new_store_contents, *args) -> html.Div: - return self.cohp.standard_layout diff --git a/crystal_toolkit/components/structure.py b/crystal_toolkit/components/structure.py index c7cd9c90..459e024d 100644 --- a/crystal_toolkit/components/structure.py +++ b/crystal_toolkit/components/structure.py @@ -19,7 +19,6 @@ from pymatgen.analysis.local_env import NearNeighbors from pymatgen.core import Composition, Molecule, Species, Structure from pymatgen.core.periodic_table import DummySpecie -from pymatgen.io.lobster.lobsterenv import LobsterNeighbors from pymatgen.io.vasp.sets import MPRelaxSet from pymatgen.symmetry.analyzer import SpacegroupAnalyzer @@ -56,10 +55,7 @@ class StructureMoleculeComponent(MPComponent): """ available_bonding_strategies = frozendict( - { - **{subcls.__name__: subcls for subcls in NearNeighbors.__subclasses__()}, - "LobsterNeighbors": LobsterNeighbors, - } + {subcls.__name__: subcls for subcls in NearNeighbors.__subclasses__()} ) default_scene_settings = frozendict( @@ -970,7 +966,6 @@ def _preprocess_input_to_graph( valid_bond_strategies = ( StructureMoleculeComponent.available_bonding_strategies ) - if bonding_strategy not in valid_bond_strategies: raise ValueError( "Bonding strategy not supported. Please supply a name of a NearNeighbor " diff --git a/pyproject.toml b/pyproject.toml index d9435fd7..86b376b0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,6 @@ fermi = ["ifermi", "pyfftw"] phonon = ["phonopy"] vtk = ["dash-vtk"] localenv = ["dscribe"] -lobster = ["lobsterpy"] figures = ["kaleido"] dev = [ "black",