diff --git a/aiidalab_widgets_base/__init__.py b/aiidalab_widgets_base/__init__.py index d790ca27c..dd0301946 100644 --- a/aiidalab_widgets_base/__init__.py +++ b/aiidalab_widgets_base/__init__.py @@ -79,4 +79,4 @@ "viewer", ] -__version__ = "1.1.0" +__version__ = "1.1.1" diff --git a/aiidalab_widgets_base/bug_report.py b/aiidalab_widgets_base/bug_report.py index ef0cd13b5..e92056ead 100644 --- a/aiidalab_widgets_base/bug_report.py +++ b/aiidalab_widgets_base/bug_report.py @@ -108,7 +108,7 @@ def _convert_ansi_codes_to_html(msg): return converter.produce_headers().strip() + converter.convert(msg, full=False) -def _format_truncated_traceback(traceback, max_num_chars=3000): +def _format_truncated_traceback(traceback, max_num_chars=2000): """Truncate the traceback to the given character length.""" n = 0 for i, line in enumerate(reversed(traceback)): @@ -145,9 +145,9 @@ def create_github_issue_exception_handler(exception_type, exception, traceback): exception_type=str(exception_type.__name__) ), "body": BUG_REPORT_BODY.format( - # Truncate the traceback to a maximum of 3000 characters + # Truncate the traceback to a maximum of 2000 characters # and strip all ansi control characters: - traceback=_format_truncated_traceback(traceback, 3000), + traceback=_format_truncated_traceback(traceback, 2000), # Determine and format the environment fingerprint to be # included with the bug report: environment_fingerprint="\n".join( diff --git a/aiidalab_widgets_base/databases.py b/aiidalab_widgets_base/databases.py index dbe31306f..259d2c4ae 100644 --- a/aiidalab_widgets_base/databases.py +++ b/aiidalab_widgets_base/databases.py @@ -150,13 +150,12 @@ class OptimadeQueryWidget(ipw.VBox): "cod", "tcod", "nmd", - "omdb", "oqmd", "aflow", "matcloud", - "httk", "mpds", "necro", + "jarvis", ] _skip_databases = {"Materials Cloud": ["optimade-sample", "li-ion-conductors"]} _database_grouping = { diff --git a/aiidalab_widgets_base/structures.py b/aiidalab_widgets_base/structures.py index f609a0b97..33d493b25 100644 --- a/aiidalab_widgets_base/structures.py +++ b/aiidalab_widgets_base/structures.py @@ -265,7 +265,12 @@ def _convert_to_structure_node(self, structure): # If the input_structure trait is set to Atoms object, structure node must be created from it. if isinstance(structure, Atoms): - return structure_node_type(ase=structure) + # If the Atoms object was created by SmilesWidget, + # attach its SMILES code as an extra. + structure_node = structure_node_type(ase=structure) + if "smiles" in structure.info: + structure_node.set_extra("smiles", structure.info["smiles"]) + return structure_node # If the input_structure trait is set to AiiDA node, check what type if isinstance(structure, Data): @@ -653,17 +658,20 @@ def __init__(self, title=""): super().__init__([self.smiles, self.create_structure_btn, self.output]) - def make_ase(self, species, positions): + def _make_ase(self, species, positions, smiles): """Create ase Atoms object.""" # Get the principal axes and realign the molecule along z-axis. positions = PCA(n_components=3).fit_transform(positions) atoms = Atoms(species, positions=positions, pbc=True) atoms.cell = np.ptp(atoms.positions, axis=0) + 10 atoms.center() + # We're attaching this info so that it + # can be later stored as an extra on AiiDA Structure node. + atoms.info["smiles"] = smiles return atoms - def _pybel_opt(self, smile, steps): + def _pybel_opt(self, smiles, steps): """Optimize a molecule using force field and pybel (needed for complex SMILES).""" from openbabel import openbabel as ob from openbabel import pybel as pb @@ -671,7 +679,7 @@ def _pybel_opt(self, smile, steps): obconversion = ob.OBConversion() obconversion.SetInFormat("smi") obmol = ob.OBMol() - obconversion.ReadString(obmol, smile) + obconversion.ReadString(obmol, smiles) pbmol = pb.Molecule(obmol) pbmol.make3D(forcefield="uff", steps=50) @@ -685,15 +693,15 @@ def _pybel_opt(self, smile, steps): f_f.GetCoordinates(pbmol.OBMol) species = [chemical_symbols[atm.atomicnum] for atm in pbmol.atoms] positions = np.asarray([atm.coords for atm in pbmol.atoms]) - return self.make_ase(species, positions) + return self._make_ase(species, positions, smiles) - def _rdkit_opt(self, smile, steps): + def _rdkit_opt(self, smiles, steps): """Optimize a molecule using force field and rdkit (needed for complex SMILES).""" from rdkit import Chem from rdkit.Chem import AllChem - smile = smile.replace("[", "").replace("]", "") - mol = Chem.MolFromSmiles(smile) + smiles = smiles.replace("[", "").replace("]", "") + mol = Chem.MolFromSmiles(smiles) mol = Chem.AddHs(mol) AllChem.EmbedMolecule(mol, maxAttempts=20, randomSeed=42) @@ -701,14 +709,14 @@ def _rdkit_opt(self, smile, steps): positions = mol.GetConformer().GetPositions() natoms = mol.GetNumAtoms() species = [mol.GetAtomWithIdx(j).GetSymbol() for j in range(natoms)] - return self.make_ase(species, positions) + return self._make_ase(species, positions, smiles) - def mol_from_smiles(self, smile, steps=10000): + def _mol_from_smiles(self, smiles, steps=10000): """Convert SMILES to ase structure try rdkit then pybel""" try: - return self._rdkit_opt(smile, steps) + return self._rdkit_opt(smiles, steps) except ValueError: - return self._pybel_opt(smile, steps) + return self._pybel_opt(smiles, steps) def _on_button_pressed(self, change): # pylint: disable=unused-argument """Convert SMILES to ase structure when button is pressed.""" @@ -719,7 +727,7 @@ def _on_button_pressed(self, change): # pylint: disable=unused-argument self.output.value = "Screening possible conformers {}".format( self.SPINNER ) # font-size:20em; - self.structure = self.mol_from_smiles(self.smiles.value) + self.structure = self._mol_from_smiles(self.smiles.value) self.output.value = "" @default("structure")