Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse symmetry info and show in cell tab #328

Merged
merged 13 commits into from
Sep 6, 2022
14 changes: 14 additions & 0 deletions aiidalab_widgets_base/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
"""Some utility functions used acrross the repository."""
import threading
from typing import Any, Tuple

import ipywidgets as ipw
import more_itertools as mit
import numpy as np
import traitlets
from aiida.plugins import DataFactory
from ase import Atoms
from ase.io import read

CifData = DataFactory("cif") # pylint: disable=invalid-name
Expand Down Expand Up @@ -166,3 +168,15 @@ class StatusHTML(_StatusWidgetMixin, ipw.HTML):
@traitlets.observe("message")
def _observe_message(self, change):
self.show_temporary_message(change["new"])


def ase2spglib(ase_structure: Atoms) -> Tuple[Any, Any, Any]:
"""
Convert ase Atoms instance to spglib cell in the format defined at
https://spglib.github.io/spglib/python-spglib.html#crystal-structure-cell
"""
lattice = ase_structure.get_cell()
positions = ase_structure.get_scaled_positions()
numbers = ase_structure.get_atomic_numbers()

return (lattice, positions, numbers)
csadorf marked this conversation as resolved.
Show resolved Hide resolved
60 changes: 47 additions & 13 deletions aiidalab_widgets_base/viewers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import ipywidgets as ipw
import nglview
import numpy as np
import spglib
import traitlets
from aiida.cmdline.utils.common import get_workchain_report
from aiida.orm import Node
Expand Down Expand Up @@ -42,7 +43,7 @@

from .dicts import Colors, Radius
from .misc import CopyToClipboardButton, ReversePolishNotation
from .utils import list_to_string_range, string_range_to_list
from .utils import ase2spglib, list_to_string_range, string_range_to_list

AIIDA_VIEWER_MAPPING = dict()

Expand Down Expand Up @@ -177,7 +178,7 @@ class _StructureDataBaseViewer(ipw.VBox):
def __init__(
self,
configure_view=True,
configuration_tabs=["Selection", "Appearance", "Cell", "Download"],
configuration_tabs=None,
default_camera="orthographic",
**kwargs,
):
Expand All @@ -192,9 +193,9 @@ def __init__(
view_box = ipw.VBox([self._viewer])

configuration_tabs_map = {
"Cell": self._cell_tab(),
"Selection": self._selection_tab(),
"Appearance": self._appearance_tab(),
"Cell": self._cell_tab(),
"Download": self._download_tab(),
}

Expand All @@ -207,17 +208,21 @@ def __init__(
configuration_tabs.clear()

# Constructing configuration box
if configuration_tabs is None:
configuration_tabs = ["Selection", "Appearance", "Cell", "Download"]

if len(configuration_tabs) != 0:
configuration_box = ipw.Tab(
self.selection_tab_idx = configuration_tabs.index("Selection")
self.configuration_box = ipw.Tab(
layout=ipw.Layout(flex="1 1 auto", width="auto")
)
configuration_box.children = [
self.configuration_box.children = [
configuration_tabs_map[tab_title] for tab_title in configuration_tabs
]

for i, title in enumerate(configuration_tabs):
configuration_box.set_title(i, title)
children = [ipw.HBox([view_box, configuration_box])]
self.configuration_box.set_title(i, title)
children = [ipw.HBox([view_box, self.configuration_box])]
view_box.layout = {"width": "60%"}
else:
children = [view_box]
Expand Down Expand Up @@ -335,7 +340,8 @@ def change_camera(change):

@observe("cell")
def _observe_cell(self, _=None):
if self.cell:
# only update cell info when it is a 3D structure.
Copy link
Contributor

@danielhollas danielhollas Sep 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder why 2D structures are not supported?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two reasons that make this not easy to show. First, if it is a 2D structure, the spglib does not work well with fining the symmetry for 2D structure which has an different set of space group. Secondly, I have no idea what to show for the cell information, do we still show a, b, c with c set to a large value as mimicking the vacuum layer, or don't show the c and beta in the tab.

if self.cell and all(self.structure.pbc):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to either completely hide the Cell tab when the structure is not periodic, or at least explicitly indicate that inside the tab.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for mentioning this. Actually, I made some effort to implement that but failed. The reason is that the code either hides the tab or adds a message inside the cell tab makes more sense to put in _cell_tab method, but in there the self.structure trait is not being set yet.
Adding that too this PR will bring more coupling. But you are right, if it is okay with you, I'll open an issue for this and leave it to the future after we have decoupled the base structure data view.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@unkcpz did you open the issue? If yes, please mention it here for the record

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just open the issue #351 for this.

self.cell_a.value = "<i><b>a</b></i>: {:.4f} {:.4f} {:.4f}".format(
*self.cell.array[0]
)
Expand All @@ -359,6 +365,14 @@ def _observe_cell(self, _=None):
self.cell_alpha.value = f"&alpha;: {self.cell.angles()[0]:.4f}"
self.cell_beta.value = f"&beta;: {self.cell.angles()[1]:.4f}"
self.cell_gamma.value = f"&gamma;: {self.cell.angles()[2]:.4f}"

spglib_structure = ase2spglib(self.structure)
symmetry_dataset = spglib.get_symmetry_dataset(
spglib_structure, symprec=1e-5, angle_tolerance=1.0
)

self.cell_spacegroup.value = f"Spacegroup: {symmetry_dataset['international']} (No.{symmetry_dataset['number']})"
self.cell_hall.value = f"Hall: {symmetry_dataset['hall']} (No.{symmetry_dataset['hall_number']})"
else:
self.cell_a.value = "<i><b>a</b></i>:"
self.cell_b.value = "<i><b>b</b></i>:"
Expand All @@ -385,10 +399,14 @@ def _cell_tab(self):
self.cell_beta = ipw.HTML()
self.cell_gamma = ipw.HTML()

self.cell_spacegroup = ipw.HTML()
self.cell_hall = ipw.HTML()

self._observe_cell()

return ipw.VBox(
[
ipw.HTML("Length unit: angstrom (Å)"),
ipw.HBox(
[
ipw.VBox(
Expand All @@ -410,12 +428,24 @@ def _cell_tab(self):
),
]
),
ipw.VBox(
ipw.HBox(
[
ipw.HTML("Angles:"),
self.cell_alpha,
self.cell_beta,
self.cell_gamma,
ipw.VBox(
[
ipw.HTML("Angles:"),
self.cell_alpha,
self.cell_beta,
self.cell_gamma,
]
),
ipw.VBox(
[
ipw.HTML("Symmetry information:"),
self.cell_spacegroup,
self.cell_hall,
],
layout={"margin": "0 0 0 50px"},
),
]
),
]
Expand Down Expand Up @@ -647,6 +677,10 @@ def _observe_selection(self, _=None):
self.highlight_atoms(self.selection)
self._selected_atoms.value = list_to_string_range(self.selection, shift=1)

# if atom is selected from nglview, shift to selection tab
if self._selected_atoms.value:
self.configuration_box.selected_index = self.selection_tab_idx

def apply_selection(self, _=None):
"""Apply selection specified in the text field."""
selection_string = self._selected_atoms.value
Expand Down
1 change: 1 addition & 0 deletions metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"more_itertools~=8.0",
"nglview~=3.0",
"numpy~=1.17",
"spglib~=1.16",
"optimade-client==2021.5.7",
"pandas~=1.0",
"scikit-learn~=0.24",
Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ install_requires =
optimade-client==2022.4.20
pandas~=1.0
scikit-learn~=0.24
spglib~=1.16
vapory~=0.1.2
python_requires = >=3.7
include_package_data = True
Expand Down