Source code for sknano.core.molecules._molecules

# -*- coding: utf-8 -*-
"""
==============================================================================
Base class for structure molecules (:mod:`sknano.core.molecules._molecules`)
==============================================================================

.. currentmodule:: sknano.core.molecules._molecules

"""
from __future__ import absolute_import, division, print_function
from __future__ import unicode_literals
__docformat__ = 'restructuredtext en'

from collections import OrderedDict
from operator import attrgetter

import numpy as np

from sknano.core import UserList, xyz
from sknano.core.math import Vector, transformation_matrix
from sknano.core.geometric_regions import Cuboid  # , Rectangle

__all__ = ['Molecules']


[docs]class Molecules(UserList): """Base class for collection of `Molecule` objects. Parameters ---------- molecules : {None, sequence, `Molecules`}, optional if not `None`, then a list of `Molecule` instance objects or an existing `Molecules` instance object. """ _moleculeattrs = [] def __init__(self, molecules=None): super().__init__(initlist=molecules) def __str__(self): return repr(self) def __repr__(self): """Return canonical string representation of `Molecules`.""" return "Molecules(molecules={!r})".format(self.data)
[docs] def sort(self, key=attrgetter('id'), reverse=False): super().sort(key=key, reverse=reverse)
@property def Nmolecules(self): """Number of molecules in `Molecules`.""" return len(self) @property def CM(self): """Center-of-Mass coordinates of `Molecules`. Returns ------- ndarray 3-element ndarray specifying center-of-mass coordinates of `Molecules`. """ masses = np.asarray([self.masses]) coords = self.coords MxR = masses.T * coords return Vector(np.sum(MxR, axis=0) / np.sum(masses)) @property def M(self): """Total mass of `Molecules`.""" #return math.fsum(self.masses) return self.masses.sum() @property def coords(self): """Return list of `Molecule` coordinates.""" return np.asarray([molecule.r for molecule in self]) @property def masses(self): """Return list of `Molecule` masses.""" return np.asarray([molecule.m for molecule in self]) @property def symbols(self): """Return list of `Molecule` symbols.""" return np.asarray([molecule.symbol for molecule in self]) @property def x(self): """Return :math:`x` coordinates of `Molecule` objects as array.""" return self.coords[:,0] @property def y(self): """Return :math:`y` coordinates of `Molecule` objects as array.""" return self.coords[:,1] @property def z(self): """Return :math:`z` coordinates of `Molecule` objects as array.""" return self.coords[:,2] @property def bounds(self): """Return bounds of `Molecules`.""" return Cuboid(pmin=[self.x.min(), self.y.min(), self.z.min()], pmax=[self.x.max(), self.y.max(), self.z.max()])
[docs] def center_CM(self, axes=None): """Center molecules on CM coordinates.""" dr = -self.CM self.translate(dr)
[docs] def clip_bounds(self, region, center_before_clipping=False): """Remove molecules outside the given limits along given dimension. Parameters ---------- region : :class:`~sknano.core.geometric_regions.`GeometricRegion` """ CM0 = None if center_before_clipping: CM0 = self.CM self.translate(-CM0) self.data = \ np.asarray(self)[np.logical_and( np.logical_and( self.x <= region.limits['x']['max'], np.logical_and( self.y <= region.limits['y']['max'], self.z <= region.limits['z']['max'])), np.logical_and( self.x >= region.limits['x']['min'], np.logical_and( self.y >= region.limits['y']['min'], self.z >= region.limits['z']['min'])))].tolist() if CM0 is not None: self.translate(CM0)
[docs] def filter(self, condition, invert=False): """Filter `Molecules` by `condition`. Parameters ---------- condition : array_like, bool invert : bool, optional Returns ------- filtered_molecules : `Molecules` """ return self.__class__(molecules=np.asarray(self)[condition].tolist())
[docs] def get_molecules(self, asarray=False): """Return list of `Molecules`. Parameters ---------- asarray : bool, optional Returns ------- sequence or ndarray """ if asarray: return np.asarray(self) else: return self
[docs] def get_coords(self, asdict=False): """Return molecule coords. Parameters ---------- asdict : bool, optional Returns ------- coords : :py:class:`python:~collections.OrderedDict` or ndarray """ coords = self.coords if asdict: return OrderedDict(list(zip(xyz, coords.T))) else: return coords
[docs] def rezero_coords(self, epsilon=1.0e-10): """Alias for :meth:`Molecules.rezero`.""" self.rezero(epsilon=epsilon)
[docs] def rezero_xyz(self, epsilon=1.0e-10): self.rezero(epsilon=epsilon)
[docs] def rezero(self, epsilon=1.0e-10): """Set really really small coordinates to zero. Set all coordinates with absolute value less than epsilon to zero. Parameters ---------- epsilon : float smallest allowed absolute value of any :math:`x,y,z` component. """ [molecule.rezero(epsilon=epsilon) for molecule in self]
[docs] def rotate(self, angle=None, axis=None, anchor_point=None, rot_point=None, from_vector=None, to_vector=None, degrees=False, transform_matrix=None, verbose=False, **kwargs): """Rotate `Molecule` position vectors. Parameters ---------- angle : float axis : :class:`~sknano.core.math.Vector`, optional anchor_point : :class:`~sknano.core.math.Point`, optional rot_point : :class:`~sknano.core.math.Point`, optional from_vector, to_vector : :class:`~sknano.core.math.Vector`, optional degrees : bool, optional transform_matrix : :class:`~numpy:numpy.ndarray` """ if transform_matrix is None: transform_matrix = \ transformation_matrix(angle=angle, axis=axis, anchor_point=anchor_point, rot_point=rot_point, from_vector=from_vector, to_vector=to_vector, degrees=degrees, verbose=verbose, **kwargs) [molecule.rotate(transform_matrix=transform_matrix) for molecule in self]
[docs] def translate(self, t, fix_anchor_points=True): """Translate `Molecule` position vectors by :class:`Vector` `t`. Parameters ---------- t : :class:`Vector` fix_anchor_points : bool, optional """ [molecule.translate(t, fix_anchor_point=fix_anchor_points) for molecule in self]