From fb9b268c7cf549260c479d7afafef472d88e8908 Mon Sep 17 00:00:00 2001 From: Aaron Virshup Date: Fri, 28 Jul 2017 17:35:26 -0700 Subject: [PATCH 1/2] Add misc. helpers for nbmolviz --- moldesign/helpers/widgets.py | 2 +- moldesign/molecules/bonds.py | 32 ++++++++++++++++++++++++++++++++ moldesign/orbitals/wfn.py | 8 ++++++-- moldesign/units/quantity.py | 5 ++++- 4 files changed, 43 insertions(+), 4 deletions(-) diff --git a/moldesign/helpers/widgets.py b/moldesign/helpers/widgets.py index 1b84124..5c4fe97 100644 --- a/moldesign/helpers/widgets.py +++ b/moldesign/helpers/widgets.py @@ -59,7 +59,7 @@ def not_installed_method(*args, **kwargs): try: - from nbmolviz.uibase import Logger, display_log + from nbmolviz.uielements import Logger, display_log exports_names('Logger', 'display_log') except ImportError: diff --git a/moldesign/molecules/bonds.py b/moldesign/molecules/bonds.py index 6a2ae59..c0f6063 100644 --- a/moldesign/molecules/bonds.py +++ b/moldesign/molecules/bonds.py @@ -25,6 +25,7 @@ from . import toplevel from .. import units as u from .. import mathutils +from .. import geom @toplevel @@ -95,6 +96,12 @@ def order(self, order): if atom.molecule is self.molecule: atom.bond_graph[nbr] = order + @property + def exists(self): + """ bool: whether or not this bond exists + """ + return self.order is not None + @property def type(self): return self.a1.symbol + self.SYMBOLS.get(self.order, u'?ΜΆ') + self.a2.symbol @@ -129,6 +136,11 @@ def partner(self, atom): def length(self): return self.a1.distance(self.a2) + @length.setter + def length(self, value): + geom.set_distance(self.a1, self.a2, value, + adjustmol=self.exists and not self.is_cyclic) + @property def name(self): """ str: name of the bond """ @@ -151,6 +163,26 @@ def molecule(self): def midpoint(self): return (self.a1.position + self.a2.position) / 2.0 + @property + def is_cyclic(self): + """ + bool: True if this bond is in one or more rings + """ + visited = set([self.a2]) + + def check_for_cycles(atom): + visited.add(atom) + for nbr in atom.bond_graph: + if nbr is self.a2 and atom is not self.a1: + return True + if nbr not in visited: + in_cycle = check_for_cycles(nbr) + if in_cycle: + return True + return False # if here, not in a cycle + check_for_cycles(self.a1) + + def align(self, other, centered=True): """ Rotates the entire molecule to align this bond with another object. diff --git a/moldesign/orbitals/wfn.py b/moldesign/orbitals/wfn.py index c7daa14..926a741 100644 --- a/moldesign/orbitals/wfn.py +++ b/moldesign/orbitals/wfn.py @@ -19,7 +19,7 @@ import numpy as np import copy -from . import MolecularOrbitals +from . import Orbital, MolecularOrbitals from ..utils import DotDict @@ -73,8 +73,12 @@ def __init__(self, mol, num_electrons, self.positions = positions.copy() if self.aobasis is not None: - self.orbitals['atomic'] = self.aobasis self.aobasis.wfn = self + atomic_orbs = [Orbital(c, basis=self.aobasis, wfn=self, name=bf.name) + for c,bf in zip(np.identity(len(self.aobasis)), self.aobasis)] + self.orbitals.atomic = MolecularOrbitals(atomic_orbs, wfn=self, + basis=self.aobasis, + orbtype='atomic') for orb in self.aobasis.orbitals: orb.wfn = self diff --git a/moldesign/units/quantity.py b/moldesign/units/quantity.py index 1c10e2f..8c9d481 100644 --- a/moldesign/units/quantity.py +++ b/moldesign/units/quantity.py @@ -125,7 +125,10 @@ def __deepcopy__(self, memo): return result def __hash__(self): - return hash((self._magnitude, str(self.units))) + m = self._magnitude + if isinstance(m, np.ndarray) and m.shape == (): + m = float(m) + return hash((m, str(self.units))) def __setitem__(self, key, value): from . import array as quantityarray From 04f91602bd468c27504c5be2d35621c1d04585c7 Mon Sep 17 00:00:00 2001 From: Aaron Virshup Date: Wed, 2 Aug 2017 21:29:37 -0700 Subject: [PATCH 2/2] Small method tweaks --- moldesign/_tests/test_gaussian_math.py | 2 +- moldesign/utils/descriptors.py | 21 ++++++++++++++------- moldesign/widgets.py | 9 +++++---- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/moldesign/_tests/test_gaussian_math.py b/moldesign/_tests/test_gaussian_math.py index db80898..d2fc817 100644 --- a/moldesign/_tests/test_gaussian_math.py +++ b/moldesign/_tests/test_gaussian_math.py @@ -177,7 +177,7 @@ def _assert_numerical_analytical_overlaps_match(g1, g2): with np.errstate(under='ignore'): prodvals = g1(allpoints) * g2(allpoints) numsum = prodvals.sum() * grid.dx * grid.dy * grid.dz - helpers.assert_almost_equal(numsum, olap, decimal=5) + helpers.assert_almost_equal(numsum, olap, decimal=4) @pytest.mark.parametrize('withunits', [False, True]) diff --git a/moldesign/utils/descriptors.py b/moldesign/utils/descriptors.py index 206ef6a..b46c5e0 100644 --- a/moldesign/utils/descriptors.py +++ b/moldesign/utils/descriptors.py @@ -19,7 +19,7 @@ class Alias(object): """ - Descriptor that calls a child object's method. + Descriptor that delegates to a child's attribute or method. e.g. >>> class A(object): >>> childkeys = Alias('child.keys') @@ -27,21 +27,28 @@ class Alias(object): >>> >>> a = A() >>> a.child['key'] = 'value' - >>> a.childkeys() #calls a.child.keys(), returns ['key'] + >>> a.childkeys() # calls a.child.keys(), returns ['key'] ['key'] """ - def __init__(self, objmethod): - objname, methodname = objmethod.split('.') + def __init__(self, objattr): + objname, attrname = objattr.split('.') self.objname = objname - self.methodname = methodname + self.attrname = attrname def __get__(self, instance, owner): if instance is None: assert owner is not None - return _unbound_getter(self.objname, self.methodname) + return _unbound_getter(self.objname, self.attrname) else: proxied = getattr(instance, self.objname) - return getattr(proxied,self.methodname) + return getattr(proxied, self.attrname) + + def __set__(self, instance, value): + if instance is None: + raise NotImplementedError() + else: + proxied = getattr(instance, self.objname) + setattr(proxied, self.attrname, value) def _unbound_getter(objname, methodname): diff --git a/moldesign/widgets.py b/moldesign/widgets.py index d3d9165..a0f99f7 100644 --- a/moldesign/widgets.py +++ b/moldesign/widgets.py @@ -19,11 +19,12 @@ from .helpers.widgets import nbmolviz_installed if nbmolviz_installed: - from nbmolviz.widgets import BondSelector, GeometryBuilder, ResidueSelector, Symmetrizer + from nbmolviz.widgets import (BondSelector, GeometryBuilder, ResidueSelector, Symmetrizer, + AtomSelector) from nbmolviz.widgets.computeconfig import configure, about else: from .helpers.widgets import not_installed_method - BondSelector = GeometryBuilder = ResidueSelector = Symmetrizer = configure = about = \ - not_installed_method + BondSelector = GeometryBuilder = ResidueSelector = AtomSelector = Symmetrizer = configure \ + = about = not_installed_method -__all__ = 'BondSelector GeometryBuilder ResidueSelector Symmetrizer'.split() +__all__ = 'BondSelector GeometryBuilder ResidueSelector Symmetrizer AtomSelector'.split()