diff --git a/aiidalab_widgets_base/structures.py b/aiidalab_widgets_base/structures.py
index 48af51852..b90b7a6c0 100644
--- a/aiidalab_widgets_base/structures.py
+++ b/aiidalab_widgets_base/structures.py
@@ -7,11 +7,11 @@
import pathlib
import tempfile
from collections import OrderedDict
+from copy import deepcopy
import ase
import ipywidgets as ipw
import numpy as np
-from copy import deepcopy
# spglib for cell converting
import spglib
@@ -30,7 +30,19 @@
from ase import Atom, Atoms
from ase.data import chemical_symbols, covalent_radii
from sklearn.decomposition import PCA
-from traitlets import Bool,Dict, Instance, Int, List, Unicode, Union, default, dlink, link, observe
+from traitlets import (
+ Bool,
+ Dict,
+ Instance,
+ Int,
+ List,
+ Unicode,
+ Union,
+ default,
+ dlink,
+ link,
+ observe,
+)
# Local imports
from .data import LigandSelectorWidget
@@ -57,7 +69,7 @@ class StructureManagerWidget(ipw.VBox):
structure = Union([Instance(Atoms), Instance(Data)], allow_none=True)
structure_node = Instance(Data, allow_none=True, read_only=True)
node_class = Unicode()
- #brand_new_structure = Bool()
+ # brand_new_structure = Bool()
SUPPORTED_DATA_FORMATS = {"CifData": "cif", "StructureData": "structure"}
@@ -161,12 +173,11 @@ def _structure_importers(self, importers):
if len(importers) == 1:
# Assigning a function which will be called when importer provides a structure.
dlink((importers[0], "structure"), (self, "input_structure"))
- #if importers[0].has_trait("brand_new_structure"):
+ # if importers[0].has_trait("brand_new_structure"):
# link((importers[0], "brand_new_structure"), (self.viewer, "brand_new_structure"))
# link((importers[0], "brand_new_structure"), (self, "brand_new_structure"))
return importers[0]
-
# Otherwise making one tab per importer.
importers_tab = ipw.Tab()
@@ -175,7 +186,7 @@ def _structure_importers(self, importers):
# Labeling tabs.
importers_tab.set_title(i, importer.title)
dlink((importer, "structure"), (self, "input_structure"))
- #if importer.has_trait("brand_new_structure"):
+ # if importer.has_trait("brand_new_structure"):
# link((importer, "brand_new_structure"), (self.viewer, "brand_new_structure"))
# link((importer, "brand_new_structure"), (self, "brand_new_structure"))
return importers_tab
@@ -201,7 +212,7 @@ def _structure_editors(self, editors):
editors_tab.set_title(i, editor.title)
link((editor, "structure"), (self, "structure"))
if editor.has_trait("selection"):
- link((editor, "selection"), (self.viewer, "selection"))
+ link((editor, "selection"), (self.viewer, "selection"))
if editor.has_trait("camera_orientation"):
dlink(
(self.viewer._viewer, "_camera_orientation"),
@@ -343,7 +354,6 @@ def _observe_input_structure(self, change):
else:
self.structure = None
-
@observe("structure")
def _structure_changed(self, change=None):
"""Perform some operations that depend on the value of `structure` trait.
@@ -351,7 +361,7 @@ def _structure_changed(self, change=None):
This function enables/disables `btn_store` widget if structure is provided/set to None.
Also, the function sets `structure_node` trait to the selected node type.
"""
-
+
if not self.structure_set_by_undo:
self.history.append(change["new"])
@@ -692,7 +702,7 @@ class SmilesWidget(ipw.VBox):
"""Convert SMILES into 3D structure."""
structure = Instance(Atoms, allow_none=True)
- #brand_new_structure = Bool()
+ # brand_new_structure = Bool()
SPINNER = """"""
@@ -1195,8 +1205,7 @@ def disable_element(_=None):
]
)
-
- def find_index(self,list_of_lists, element):
+ def find_index(self, list_of_lists, element):
for i, x in enumerate(list_of_lists):
if element in x:
return i
@@ -1278,7 +1287,7 @@ def translate_dr(self, _=None, atoms=None, selection=None):
self.action_vector * self.displacement.value
)
- #self.brand_new_structure = False
+ # self.brand_new_structure = False
self.structure, self.selection = atoms, selection
@_register_structure
@@ -1289,7 +1298,7 @@ def translate_dxdydz(self, _=None, atoms=None, selection=None):
# The action.
atoms.positions[self.selection] += np.array(self.str2vec(self.dxyz.value))
- #self.brand_new_structure = False
+ # self.brand_new_structure = False
self.structure, self.selection = atoms, selection
@@ -1301,7 +1310,7 @@ def translate_to_xyz(self, _=None, atoms=None, selection=None):
geo_center = np.average(self.structure[self.selection].get_positions(), axis=0)
atoms.positions[self.selection] += self.str2vec(self.dxyz.value) - geo_center
- #self.brand_new_structure = False
+ # self.brand_new_structure = False
self.structure, self.selection = atoms, selection
@_register_structure
@@ -1316,7 +1325,7 @@ def rotate(self, _=None, atoms=None, selection=None):
rotated_subset.rotate(self.phi.value, v=vec, center=center, rotate_cell=False)
atoms.positions[list(self.selection)] = rotated_subset.positions
- #self.brand_new_structure = False
+ # self.brand_new_structure = False
self.structure, self.selection = atoms, selection
@_register_structure
@@ -1350,7 +1359,7 @@ def mirror(self, _=None, norm=None, point=None, atoms=None, selection=None):
# Mirror atoms.
atoms.positions[selection] -= 2 * projections
- #self.brand_new_structure = False
+ # self.brand_new_structure = False
self.structure, self.selection = atoms, selection
def mirror_3p(self, _=None):
@@ -1375,7 +1384,7 @@ def align(self, _=None, atoms=None, selection=None):
subset.rotate(self.action_vector, self.str2vec(self.dxyz.value), center=center)
atoms.positions[selection] = subset.positions
- #self.brand_new_structure = False
+ # self.brand_new_structure = False
self.structure, self.selection = atoms, selection
@_register_structure
@@ -1384,7 +1393,6 @@ def mod_element(self, _=None, atoms=None, selection=None):
"""Modify selected atoms into the given element."""
last_atom = atoms.get_global_number_of_atoms()
-
if self.ligand.value == 0:
for idx in self.selection:
new = Atom(self.element.value)
@@ -1399,17 +1407,15 @@ def mod_element(self, _=None, atoms=None, selection=None):
initial_ligand = self.ligand.rotate(
align_to=self.action_vector, remove_anchor=True
)
-
+
for idx in self.selection:
position = self.structure.positions[idx].copy()
lgnd = initial_ligand.copy()
lgnd.translate(position)
- atoms += lgnd
+ atoms += lgnd
new_selection = [
i for i in range(last_atom, last_atom + len(selection) * len(lgnd))
]
-
-
# the order of the traitlets below is important
self.selection = []
@@ -1428,12 +1434,8 @@ def copy_sel(self, _=None, atoms=None, selection=None):
add_atoms.translate([1.0, 0, 0])
atoms += add_atoms
-
-
new_selection = [i for i in range(last_atom, last_atom + len(selection))]
-
-
# the order of the traitlets below is important
self.selection = []
self.structure = atoms
@@ -1471,7 +1473,7 @@ def add(self, _=None, atoms=None, selection=None):
i for i in range(last_atom, last_atom + len(selection) * len(lgnd))
]
- #self.brand_new_structure = False
+ # self.brand_new_structure = False
# the order of the traitlets below is important
self.selection = []
@@ -1484,6 +1486,5 @@ def remove(self, _, atoms=None, selection=None):
"""Remove selected atoms."""
del [atoms[selection]]
-
self.selection = []
- self.structure = atoms
\ No newline at end of file
+ self.structure = atoms
diff --git a/aiidalab_widgets_base/viewers.py b/aiidalab_widgets_base/viewers.py
index 26da7f95a..4725e5a6c 100644
--- a/aiidalab_widgets_base/viewers.py
+++ b/aiidalab_widgets_base/viewers.py
@@ -2,12 +2,12 @@
# pylint: disable=no-self-use
import base64
-from hashlib import new
+import itertools
import re
import warnings
from copy import deepcopy
+from hashlib import new
-import itertools
import ipywidgets as ipw
import nglview
import numpy as np
@@ -15,7 +15,7 @@
import traitlets
from aiida.cmdline.utils.common import get_workchain_report
from aiida.cmdline.utils.query import formatting
-from aiida.orm import Node, Data
+from aiida.orm import Data, Node
from ase import Atoms, neighborlist
from ase.cell import Cell
from IPython.display import clear_output, display
@@ -51,7 +51,9 @@
from .utils import ase2spglib, list_to_string_range, string_range_to_list
AIIDA_VIEWER_MAPPING = dict()
-BOX_LAYOUT = ipw.Layout(display='flex-wrap', flex_flow='row wrap', justify_content='space-between')
+BOX_LAYOUT = ipw.Layout(
+ display="flex-wrap", flex_flow="row wrap", justify_content="space-between"
+)
def register_viewer_widget(key):
@@ -163,31 +165,39 @@ def __init__(self, parameter, downloadable=True, **kwargs):
class Representation(ipw.HBox):
"""Representation for StructureData in nglviewer
- if a structure is imported the traitlet import_structure will trigger
- initialization of the list 'list_of_representations' e.g. [[0,1,2,3,4,5]]
- the traitlet 'list_of_representations' will trigger creation of self.representations as list of Representations()
- default style set in self.representations[0].style is 'molecule'
- self.representations[0].selection is populated with all atoms
- self.update_representations() which copyes selections in the representations widgets into the list_of_representations
- ad calls self.apply_representations()
-
- apply_representations:
- creates the representation dictionary for the main structure 'rep_dict_unit', replicates teh reprsentattions in case
- of supercell and calls self.update_viewer() that creates teh nglview representattions
-
- Editors of a structure update the traitlet self.list_of_representations.
+ if a structure is imported the traitlet import_structure will trigger
+ initialization of the list 'list_of_representations' e.g. [[0,1,2,3,4,5]]
+ the traitlet 'list_of_representations' will trigger creation of self.representations as list of Representations()
+ default style set in self.representations[0].style is 'molecule'
+ self.representations[0].selection is populated with all atoms
+ self.update_representations() which copyes selections in the representations widgets into the list_of_representations
+ ad calls self.apply_representations()
+
+ apply_representations:
+ creates the representation dictionary for the main structure 'rep_dict_unit', replicates teh reprsentattions in case
+ of supercell and calls self.update_viewer() that creates teh nglview representattions
+
+ Editors of a structure update the traitlet self.list_of_representations.
"""
+
master_class = None
+
def __init__(self, indices="1..2"):
- self.selection = ipw.Text(description="atoms:",value="",style={"description_width": "initial"} )
- self.style = ipw.Dropdown(options=["molecule","surface"],value="molecule",description="mode",disabled=False)
- self.show = ipw.Checkbox(value=True,description="show",disabled=False)
+ self.selection = ipw.Text(
+ description="atoms:", value="", style={"description_width": "initial"}
+ )
+ self.style = ipw.Dropdown(
+ options=["molecule", "surface"],
+ value="molecule",
+ description="mode",
+ disabled=False,
+ )
+ self.show = ipw.Checkbox(value=True, description="show", disabled=False)
# Delete button.
self.delete_button = ipw.Button(description="Delete", button_style="danger")
self.delete_button.on_click(self.delete_myself)
-
super().__init__(
children=[
self.selection,
@@ -195,12 +205,12 @@ def __init__(self, indices="1..2"):
self.show,
self.delete_button,
]
- )
+ )
-
def delete_myself(self, _):
self.master_class.delete_representation(self)
+
class _StructureDataBaseViewer(ipw.VBox):
"""Base viewer class for AiiDA structure or trajectory objects.
@@ -212,9 +222,10 @@ class _StructureDataBaseViewer(ipw.VBox):
:type default_camera: string
"""
+
representations = traitlets.List()
natoms = Int()
- #brand_new_structure = Bool(True)
+ # brand_new_structure = Bool(True)
selection = List(Int)
selection_adv = Unicode()
supercell = List(Int)
@@ -237,28 +248,30 @@ def __init__(
self._viewer.camera = default_camera
self._viewer.observe(self._on_atom_click, names="picked")
self._viewer.stage.set_parameters(mouse_preset="pymol")
- #self.first_update_of_viewer = True
- self.natoms=0
- self.rep_dict={}
- self.rep_dict_unit={}
- self.default_representations={ "molecule": {
- "ids": "1..2",
- "aspectRatio": 3.5,
- "highlight_aspectRatio": 3.6,
- "highlight_color": "red",
- "highlight_opacity": 0.6,
- "name": "molecule",
- "type": "ball+stick",
- },
- "surface": {
- "ids": "1..2",
- "aspectRatio": 10,
- "highlight_aspectRatio": 10.1,
- "highlight_color": "green",
- "highlight_opacity": 0.6,
- "name": "surface",
- "type": "ball+stick",
- },}
+ # self.first_update_of_viewer = True
+ self.natoms = 0
+ self.rep_dict = {}
+ self.rep_dict_unit = {}
+ self.default_representations = {
+ "molecule": {
+ "ids": "1..2",
+ "aspectRatio": 3.5,
+ "highlight_aspectRatio": 3.6,
+ "highlight_color": "red",
+ "highlight_opacity": 0.6,
+ "name": "molecule",
+ "type": "ball+stick",
+ },
+ "surface": {
+ "ids": "1..2",
+ "aspectRatio": 10,
+ "highlight_aspectRatio": 10.1,
+ "highlight_color": "green",
+ "highlight_opacity": 0.6,
+ "name": "surface",
+ "type": "ball+stick",
+ },
+ }
view_box = ipw.VBox([self._viewer])
@@ -397,19 +410,26 @@ def change_camera(change):
center_button = ipw.Button(description="Center molecule")
center_button.on_click(lambda c: self._viewer.center())
-
# 5. representations buttons
- self.atoms_not_represented=ipw.Output()
+ self.atoms_not_represented = ipw.Output()
self.add_new_rep_button = ipw.Button(description="Add rep", button_style="info")
self.add_new_rep_button.on_click(self.add_representation)
apply_rep = ipw.Button(description="Apply rep")
- apply_rep.on_click(self.on_click_apply_representations)
+ apply_rep.on_click(self.on_click_apply_representations)
self.representation_output = ipw.Box(layout=BOX_LAYOUT)
return ipw.VBox(
- [supercell_selector, background_color, camera_type,
- self.add_new_rep_button, self.representation_output,self.atoms_not_represented, apply_rep, center_button]
+ [
+ supercell_selector,
+ background_color,
+ camera_type,
+ self.add_new_rep_button,
+ self.representation_output,
+ self.atoms_not_represented,
+ apply_rep,
+ center_button,
+ ]
)
def add_representation(self, _):
@@ -423,14 +443,16 @@ def delete_representation(self, representation):
self.representation_add_message.message = f"""Error: Rep. {representation} not found."""
return
- self.representations = self.representations[:index] + self.representations[index+1:]
+ self.representations = (
+ self.representations[:index] + self.representations[index + 1 :]
+ )
del representation
self.apply_representations()
@observe("representations")
def _observe_representations(self, change):
"""Update the list of representations."""
- if change['new']:
+ if change["new"]:
self.representation_output.children = change["new"]
self.representations[-1].master_class = self
else:
@@ -443,32 +465,40 @@ def update_representations(self, change=None):
if number_of_representation_widgets == 0:
self.representations = [Representation()]
# shoudl not be needed teh check of more reps in array['representations'] than actually defined reps
-
- representations = self.structure.arrays["representations"]
+
+ representations = self.structure.arrays["representations"]
for rep in set(representations):
- if rep >=0: # negative values are used for atoms not represented (different from the case of hidden representations)
- self.representations[int(rep)].selection.value = list_to_string_range([int(i) for i in np.where(representations == rep )[0]],shift=1)
+ if (
+ rep >= 0
+ ): # negative values are used for atoms not represented (different from the case of hidden representations)
+ self.representations[
+ int(rep)
+ ].selection.value = list_to_string_range(
+ [int(i) for i in np.where(representations == rep)[0]], shift=1
+ )
# empty selection field for unused representations
for rep in range(number_of_representation_widgets):
- if rep not in set([int(i) for i in representations]):
- self.representations[rep].selection.value = ""
+ if rep not in {int(i) for i in representations}:
+ self.representations[rep].selection.value = ""
self.apply_representations()
def replicate_representations(self, change=None):
- #copy representations of structure into rep of display structure
+ # copy representations of structure into rep of display structure
nrep = np.prod(self.supercell)
self.rep_dict = deepcopy(self.rep_dict_unit)
- if nrep > 1:
+ if nrep > 1:
for component in range(len(self.rep_dict_unit.keys())):
- ids = string_range_to_list(self.rep_dict_unit[component]["ids"] , shift=0)[0]
- new_ids = [i+rep*self.natoms for rep in range(nrep) for i in ids]
- self.rep_dict[component]["ids"] = list_to_string_range(new_ids,shift=0)
- self._gen_translation_indexes()
-
- def on_click_apply_representations(self,change=None):
+ ids = string_range_to_list(
+ self.rep_dict_unit[component]["ids"], shift=0
+ )[0]
+ new_ids = [i + rep * self.natoms for rep in range(nrep) for i in ids]
+ self.rep_dict[component]["ids"] = list_to_string_range(new_ids, shift=0)
+ self._gen_translation_indexes()
+
+ def on_click_apply_representations(self, change=None):
"""Updates self.displayed_structure.arrays['representations'] according to user defined representations"""
- arrayrepresentations=np.zeros(self.natoms)
+ arrayrepresentations = np.zeros(self.natoms)
for irep, rep in enumerate(self.representations):
selection = string_range_to_list(rep.selection.value, shift=-1)[0]
for index in selection:
@@ -476,46 +506,45 @@ def on_click_apply_representations(self,change=None):
self.structure.set_array("representations", arrayrepresentations)
-
self.apply_representations()
-
+ def apply_representations(self, change=None):
+ # iterate on number of representations
+ self.rep_dict_unit = {}
+ current_rep = 0
-
-
- def apply_representations(self,change=None):
- #iterate on number of representations
- self.rep_dict_unit={}
- current_rep=0
-
-
- #self.brand_new_structure=False
+ # self.brand_new_structure=False
for rep in self.representations:
# in representation dictionary indexes start from 0 so we transform '1..4' in '0..3'
idsl = string_range_to_list(rep.selection.value, shift=-1)[0]
- ids = list_to_string_range(idsl,shift=0)
+ ids = list_to_string_range(idsl, shift=0)
- self.rep_dict_unit[current_rep] = deepcopy(self.default_representations[rep.style.value])
+ self.rep_dict_unit[current_rep] = deepcopy(
+ self.default_representations[rep.style.value]
+ )
self.rep_dict_unit[current_rep]["ids"] = ids
- self.rep_dict_unit[current_rep]["name"] = "rep"+str(current_rep)
+ self.rep_dict_unit[current_rep]["name"] = "rep" + str(current_rep)
- current_rep+=1
- missing_atoms = set([int(i) for i in np.where(self.structure.arrays["representations"]<0)[0]])
+ current_rep += 1
+ missing_atoms = {
+ int(i) for i in np.where(self.structure.arrays["representations"] < 0)[0]
+ }
if missing_atoms:
self.atoms_not_represented.clear_output()
with self.atoms_not_represented:
- print("Atoms excluded from representations: ",list_to_string_range(list(missing_atoms),shift=1))
+ print(
+ "Atoms excluded from representations: ",
+ list_to_string_range(list(missing_atoms), shift=1),
+ )
else:
self.atoms_not_represented.clear_output()
- #print("before calling replicate the rep dict is",self.rep_dict_unit)
+ # print("before calling replicate the rep dict is",self.rep_dict_unit)
self.replicate_representations()
self.update_viewer()
- #if self.first_update_of_viewer:
- #self.first_update_of_viewer = self.orient_z_up()
+ # if self.first_update_of_viewer:
+ # self.first_update_of_viewer = self.orient_z_up()
self.orient_z_up()
-
-
@observe("cell")
def _observe_cell(self, _=None):
# only update cell info when it is a 3D structure.
@@ -813,20 +842,19 @@ def _gen_translation_indexes(self):
and
{(0,0):0,(0,1):1,(0,2):2,(1,0):3,(2,0):4,(2,1):5,(2,2):6,(2,3):7,(2,4):8,(2,5):9}
"""
-
self._translate_i_glob_loc = {}
- self._translate_i_loc_glob = {}
+ self._translate_i_loc_glob = {}
for component in range(len(self.rep_dict.keys())):
comp_i = 0
ids = list(
- string_range_to_list(self.rep_dict[component]["ids"], shift=0)[0])
+ string_range_to_list(self.rep_dict[component]["ids"], shift=0)[0]
+ )
for i_g in ids:
self._translate_i_glob_loc[i_g] = (component, comp_i)
self._translate_i_loc_glob[(component, comp_i)] = i_g
comp_i += 1
-
def _translate_glob_loc(self, indexes):
"""From global index to indexes of different components."""
all_comp = [list() for i in range(len(self.rep_dict.keys()))]
@@ -845,29 +873,24 @@ def _on_atom_click(self, _=None):
if self.rep_dict:
component = self._viewer.picked["component"]
-
- index = self._translate_i_loc_glob[(component, index)]
-
+
+ index = self._translate_i_loc_glob[(component, index)]
+
selection = self.selection.copy()
-
+
if selection:
- if index not in selection :
+ if index not in selection:
selection.append(index)
else:
selection.remove(index)
else:
selection = [index]
-
- #selection_unit = [i for i in selection if i < self.natoms]
- self.selection = selection
- #self.selection = selection_unit
-
-
- return
-
-
+ # selection_unit = [i for i in selection if i < self.natoms]
+ self.selection = selection
+ # self.selection = selection_unit
+ return
def highlight_atoms(
self,
@@ -922,23 +945,26 @@ def highlight_atoms(
opacity=opacity,
component=component,
)
+
def update_viewer(self, c=None):
with self.hold_trait_notifications():
-
+
while hasattr(self._viewer, "component_0"):
self._viewer.component_0.clear_representations()
cid = self._viewer.component_0.id
self._viewer.remove_component(cid)
- #copy representations of structure into rep of display structure
+ # copy representations of structure into rep of display structure
if self.displayed_structure:
- #print("inside displaying")
+ # print("inside displaying")
for component in range(len(self.rep_dict)):
rep_indexes = list(
- string_range_to_list(self.rep_dict[component]["ids"], shift=0)[0]
+ string_range_to_list(self.rep_dict[component]["ids"], shift=0)[
+ 0
+ ]
)
-
+
if rep_indexes:
mol = self.displayed_structure[rep_indexes]
@@ -957,15 +983,15 @@ def update_viewer(self, c=None):
self._viewer.add_licorice(opacity=1.0, component=component)
elif self.rep_dict[component]["type"] == "hyperball":
self._viewer.add_hyperball(opacity=1.0, component=component)
- #self._gen_translation_indexes()
+ # self._gen_translation_indexes()
self._viewer.add_unitcell()
self._viewer.center()
def orient_z_up(self, _=None):
try:
- #print("inside orient_z_up")
+ # print("inside orient_z_up")
if self.structure is not None:
- #print("orienting")
+ # print("orienting")
cell_z = self.structure.cell[2, 2]
com = self.structure.get_center_of_mass()
def_orientation = self._viewer._camera_orientation
@@ -992,7 +1018,7 @@ def orient_z_up(self, _=None):
return False
else:
return True
- except AttributeError:
+ except AttributeError:
return True
@default("supercell")
@@ -1017,8 +1043,8 @@ def _observe_selection(self, _=None):
self.configuration_box.selected_index = self.selection_tab_idx
def clear_selection(self, _=None):
- self.set_trait("selection", list()),
- self.set_trait("selection_adv", ""),
+ self.set_trait("selection", list()),
+ self.set_trait("selection_adv", ""),
def apply_selection(self, _=None):
"""Apply selection specified in the text field."""
@@ -1114,31 +1140,30 @@ def _valid_structure(self, change): # pylint: disable=no-self-use
"""Update structure."""
structure = change["value"]
if structure is None:
- return None # if no structure provided, the rest of the code can be skipped
+ return None # if no structure provided, the rest of the code can be skipped
if isinstance(structure, Atoms):
self.pk = None
elif isinstance(structure, Node):
self.pk = structure.pk
- structure = structure.get_ase()
+ structure = structure.get_ase()
else:
raise ValueError(
"Unsupported type {}, structure must be one of the following types: "
"ASE Atoms object, AiiDA CifData or StructureData."
)
- self.natoms= len(structure)
+ self.natoms = len(structure)
if "representations" not in structure.arrays:
print("Resetting representations")
structure.set_array("representations", np.zeros(self.natoms))
return structure
-
@observe("structure")
def _observe_structure(self, change):
"""Update displayed_structure trait after the structure trait has been modified."""
- if change["new"] is not None:
- self.set_trait("displayed_structure", change["new"].repeat(self.supercell))
+ if change["new"] is not None:
+ self.set_trait("displayed_structure", change["new"].repeat(self.supercell))
self.set_trait("cell", change["new"].cell)
self.structure = change["new"]
else:
@@ -1153,8 +1178,6 @@ def _observe_displayed_structure(self, change):
# to avoid unnecessary updates
# reactivation would require some care
-
-
def d_from(self, operand):
point = np.array([float(i) for i in operand[1:-1].split(",")])
return np.linalg.norm(self.structure.positions - point, axis=1)
@@ -1331,9 +1354,9 @@ def print_pos(pos):
return " ".join([str(i) for i in pos.round(2)])
def add_info(indx, atom):
- id_string="Id:"
- if indx >=self.natoms:
- id_string = "Id-x"+str(int(indx/self.natoms))
+ id_string = "Id:"
+ if indx >= self.natoms:
+ id_string = "Id-x" + str(int(indx / self.natoms))
return f"{id_string} {indx + 1}; Symbol: {atom.symbol}; Coordinates: ({print_pos(atom.position)})
"
# Find geometric center.
@@ -1343,13 +1366,19 @@ def add_info(indx, atom):
# Report coordinates.
if len(self.selection) == 1:
- return add_info(self.selection[0], self.displayed_structure[self.selection[0]])
+ return add_info(
+ self.selection[0], self.displayed_structure[self.selection[0]]
+ )
# Report coordinates, distance and center.
if len(self.selection) == 2:
info = ""
- info += add_info(self.selection[0], self.displayed_structure[self.selection[0]])
- info += add_info(self.selection[1], self.displayed_structure[self.selection[1]])
+ info += add_info(
+ self.selection[0], self.displayed_structure[self.selection[0]]
+ )
+ info += add_info(
+ self.selection[1], self.displayed_structure[self.selection[1]]
+ )
dist = self.displayed_structure.get_distance(*self.selection)
distv = self.displayed_structure.get_distance(*self.selection, vector=True)
info += f"Distance: {dist:.2f} ({print_pos(distv)})
Geometric center: ({geom_center})"
@@ -1376,7 +1405,9 @@ def add_info(indx, atom):
# Report dihedral angle and geometric center.
if len(self.selection) == 4:
try:
- dihedral = self.displayed_structure.get_dihedral(self.selection) * 180 / np.pi
+ dihedral = (
+ self.displayed_structure.get_dihedral(self.selection) * 180 / np.pi
+ )
dihedral_str = f"{dihedral:.2f}"
except ZeroDivisionError:
dihedral_str = "nan"