Source code for sknano.core.atoms._bonds
# -*- coding: utf-8 -*-
"""
===============================================================================
Class representation of atom bonds (:mod:`sknano.core.atoms._bonds`)
===============================================================================
.. currentmodule:: sknano.core.atoms._bonds
"""
from __future__ import absolute_import, division, print_function
from __future__ import unicode_literals
__docformat__ = 'restructuredtext en'
from functools import total_ordering
from operator import attrgetter
import numpy as np
from sknano.core import BaseClass, UserList, cyclic_pairs, dedupe
from sknano.core.math import Vector, vector as vec
import sknano.core.atoms
__all__ = ['Bond', 'Bonds']
@total_ordering
[docs]class Bond(BaseClass):
"""Abstract representation of bond between 2 `Atom` objects.
Parameters
----------
atom1, atom2 : `~sknano.core.atoms.Atom` instances
"""
def __init__(self, atom1, atom2):
super().__init__()
self.atoms = sknano.core.atoms.StructureAtoms()
self.atoms.extend([atom1, atom2])
self.fmtstr = "{atom1!r}, {atom2!r}"
def __str__(self):
"""Return nice string representation of `Bond`."""
return "Bond({!r}->{!r})".format(self.atom1.id, self.atom2.id)
def __eq__(self, other):
return self is other or (self.atoms == other.atoms)
def __lt__(self, other):
return self.length < other.length
def __dir__(self):
return ['atoms', 'atom1', 'atom2', 'vector', 'unit_vector', 'length']
@property
def atoms(self):
""":class:`~sknano.core.atoms.Atoms` in `Bond`."""
return self._atoms
@atoms.setter
def atoms(self, value):
self._atoms = value
@property
def atom1(self):
""":class:`~sknano.core.atoms.Atom` 1 in `Bond`."""
return self.atoms[0]
@property
def atom2(self):
""":class:`~sknano.core.atoms.Atom` 2 in `Bond`."""
return self.atoms[1]
@property
def centroid(self):
""":attr:`~sknano.core.atoms.XYZAtoms.centroid` of :class:`Bond` \
:attr:`~Bond.atoms`."""
return self.atoms.centroid
@property
def vector(self):
"""`Bond` :class:`~sknano.core.math.Vector`.
`Bond` :class:`~sknano.core.math.Vector` points from
:attr:`Bond.atom1` to :attr:`Bond.atom2`.
"""
return Vector(self.atom2.r - self.atom1.r, p0=self.atom1.r.p)
@property
def unit_vector(self):
"""`Bond` :attr:`~sknano.core.math.Vector.unit_vector`."""
return self.vector.unit_vector
@property
def length(self):
"""`Bond` :attr:`~sknano.core.math.Vector.length`."""
return self.vector.length
[docs] def rotate(self, **kwargs):
"""Rotate the bond by rotating the :attr:`~Bond.atoms`."""
[atom.rotate(fix_anchor_point=True, **kwargs) for atom in self.atoms]
[docs] def todict(self):
return dict(atom1=self.atom1, atom2=self.atom2)
[docs]class Bonds(UserList):
"""Base class for collection of atom `Bond`\ s.
Parameters
----------
bonds : {None, sequence, `Bonds`}, optional
if not `None`, then a list of `Bond` instance objects
"""
def __init__(self, bonds=None):
super().__init__(initlist=bonds)
self.fmtstr = "{bonds!r}"
def __repr__(self):
"""Return canonical string representation of `Bonds`."""
return "{}({})".format(self.__class__.__name__,
self.fmtstr.format(**self.todict()))
[docs] def sort(self, key=attrgetter('length'), reverse=False):
super().sort(key=key, reverse=reverse)
# def __getitem__(self, index):
# data = super().__getitem__(index)
# if isinstance(data, list):
# return self.__class__(data)
# return data
# def __setitem__(self, index, value):
# if not isinstance(value, (self.__class__, Bond)):
# if isinstance(index, slice):
# value = self.__class__(value)
# else:
# value = Bond(value)
# super().__setitem__(index, value)
@property
def fmtstr(self):
return self._fmtstr
@fmtstr.setter
def fmtstr(self, value):
self._fmtstr = value
@property
def Nbonds(self):
"""Number of `Bond`\ s in `Bonds`."""
return len(self)
@property
def vectors(self):
""":class:`~numpy:numpy.ndarray` of :attr:`~Bond.vector`\ s."""
return np.asarray([bond.vector for bond in self])
@property
def unit_vectors(self):
""":class:`~numpy:numpy.ndarray` of :attr:`~Bond.unit_vector`\ s."""
return np.asarray([bond.unit_vector for bond in self])
@property
def lengths(self):
""":class:`~numpy:numpy.ndarray` of :attr:`~Bond.length`\ s."""
return np.asarray([bond.length for bond in self])
@property
def mean_length(self):
"""Mean bond length."""
return np.mean(self.lengths)
@property
def bond_angle_pairs(self):
"""`cyclic_pairs` of `Bond`\ s."""
return cyclic_pairs(self.data)
@property
def angles(self):
""":class:`~numpy:numpy.ndarray` of `Bond` pair angles."""
return np.asarray([vec.angle(b1.vector, b2.vector) for (b1, b2) in
cyclic_pairs(self)])
@property
def mean_angle(self):
"""Mean bond angle."""
return np.mean(self.angles)
@property
def atoms(self):
"""`Atoms` :class:`python:set` in `Bonds`."""
atoms = []
[atoms.extend(bond.atoms) for bond in self]
atoms = dedupe(atoms, key=attrgetter('id'))
return sknano.core.atoms.StructureAtoms(list(atoms))
[docs] def todict(self):
return dict(bonds=self.data)