Skip to content

Commit

Permalink
Merge pull request #118 from aiidalab/release/1.0.0b10
Browse files Browse the repository at this point in the history
Release/1.0.0b10
  • Loading branch information
yakutovicha committed Aug 31, 2020
2 parents 9605264 + 186a086 commit d7a2533
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 21 deletions.
2 changes: 1 addition & 1 deletion aiidalab_widgets_base/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@
from .structures_multi import MultiStructureUploadWidget
from .viewers import viewer

__version__ = "1.0.0b9"
__version__ = "1.0.0b10"
61 changes: 43 additions & 18 deletions aiidalab_widgets_base/structures.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import ipywidgets as ipw
from traitlets import Instance, Int, List, Unicode, Union, dlink, link, default, observe

from sklearn.decomposition import PCA

# ASE imports
import ase
from ase import Atom, Atoms
Expand Down Expand Up @@ -47,7 +49,7 @@ def __init__(self, importers, viewer=None, editors=None, storable=True, node_cla
"""
Arguments:
importers(list): list of tuples each containing the displayed name of importer and the
importer object. Each object should containt 'structure' trait pointing to the imported
importer object. Each object should contain 'structure' trait pointing to the imported
structure. The trait will be linked to 'structure' trait of this class.
storable(bool): Whether to provide Store button (together with Store format)
Expand Down Expand Up @@ -314,14 +316,31 @@ def __init__(self, title='', description="Upload Structure"):
self.file_upload.observe(self._on_file_upload, names='value')
super().__init__(children=[self.file_upload, supported_formats])

def _validate_and_fix_ase_cell(self, ase_structure, vacuum_ang=10.0):
"""
Checks if the ase Atoms object has a cell set,
otherwise sets it to bounding box plus specified "vacuum" space
"""
cell = ase_structure.cell

if (np.linalg.norm(cell[0]) < 0.1 or np.linalg.norm(cell[1]) < 0.1 or np.linalg.norm(cell[2]) < 0.1):
# if any of the cell vectors is too short, consider it faulty
# set cell as bounding box + vacuum_ang
bbox = np.ptp(ase_structure.positions, axis=0)
new_structure = ase_structure.copy()
new_structure.cell = bbox + vacuum_ang
return new_structure
return ase_structure

def _on_file_upload(self, change=None):
"""When file upload button is pressed."""
for fname, item in change['new'].items():
frmt = fname.split('.')[-1]
if frmt == 'cif':
self.structure = CifData(file=io.BytesIO(item['content']))
else:
self.structure = get_ase_from_file(io.StringIO(item['content'].decode()), format=frmt)
self.structure = self._validate_and_fix_ase_cell(
get_ase_from_file(io.StringIO(item['content'].decode()), format=frmt))
self.file_upload.value.clear()
break

Expand Down Expand Up @@ -521,13 +540,13 @@ def __init__(self, title=''):
def pymol_2_ase(pymol):
"""Convert pymol object into ASE Atoms."""

asemol = Atoms()
for atm in pymol.atoms:
asemol.append(Atom(chemical_symbols[atm.atomicnum], atm.coords))
asemol.cell = np.amax(asemol.positions, axis=0) - np.amin(asemol.positions, axis=0) + [10] * 3
asemol.pbc = True
asemol.center()
return asemol
species = [chemical_symbols[atm.atomicnum] for atm in pymol.atoms]
pos = np.asarray([atm.coords for atm in pymol.atoms])
pca = PCA(n_components=3)
posnew = pca.fit_transform(pos)
atoms = Atoms(species, positions=posnew, pbc=True, cell=np.ptp(posnew, axis=0) + 10)
atoms.center()
return atoms

def _optimize_mol(self, mol):
"""Optimize a molecule using force field (needed for complex SMILES)."""
Expand All @@ -538,17 +557,16 @@ def _optimize_mol(self, mol):

self.output.value = "Screening possible conformers {}".format(self.SPINNER) #font-size:20em;

f_f = pybel._forcefields["mmff94"] # pylint: disable=protected-access
f_f = pybel._forcefields["uff"] # pylint: disable=protected-access
if not f_f.Setup(mol.OBMol):
f_f = pybel._forcefields["uff"] # pylint: disable=protected-access
f_f = pybel._forcefields["mmff94"] # pylint: disable=protected-access
if not f_f.Setup(mol.OBMol):
self.output.value = "Cannot set up forcefield"
return

# initial cleanup before the weighted search
f_f.SteepestDescent(5500, 1.0e-9)
f_f.WeightedRotorSearch(15000, 500)
f_f.ConjugateGradients(6500, 1.0e-10)
# Initial cleanup before the weighted search.
f_f.Setup(mol.OBMol)
f_f.SteepestDescent(5000, 1.0e-9)
f_f.GetCoordinates(mol.OBMol)
self.output.value = ""

Expand All @@ -563,12 +581,13 @@ def _on_button_pressed(self, change): # pylint: disable=unused-argument
if not self.smiles.value:
return

mol = pybel.readstring("smi", self.smiles.value)
mol = pybel.readstring("smiles", self.smiles.value)
self.output.value = """SMILES to 3D conversion {}""".format(self.SPINNER)
mol.make3D()
mol.addh()

pybel._builder.Build(mol.OBMol) # pylint: disable=protected-access
mol.addh()

self._optimize_mol(mol)
self.structure = self.pymol_2_ase(mol)

Expand Down Expand Up @@ -803,7 +822,13 @@ def mod_element(self, _=None):

if self.ligand.value == 0:
for idx in self.selection:
atoms[idx].symbol = self.element.value
new = Atom(self.element.value)
atoms[idx].mass = new.mass
atoms[idx].magmom = new.magmom
atoms[idx].momentum = new.momentum
atoms[idx].symbol = new.symbol
atoms[idx].tag = new.tag
atoms[idx].charge = new.charge
else:
initial_ligand = self.ligand.rotate(align_to=self.action_vector, remove_anchor=True)
for idx in self.selection:
Expand Down
5 changes: 3 additions & 2 deletions setup.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "1.0.0b9",
"version": "1.0.0b10",
"name": "aiidalab-widgets-base",
"author_email": "aiidalab@materialscloud.org",
"url": "https://github.com/aiidalab/aiidalab-widgets-base",
Expand All @@ -22,7 +22,8 @@
],
"extras_require": {
"testing": [
"aiida-core[testing]~=1.0"
"aiida-core[testing]~=1.0",
"scikit-learn==0.23.2"
],
"pre-commit": [
"pre-commit==1.17.0",
Expand Down

0 comments on commit d7a2533

Please sign in to comment.