Source code for sknano.core.atoms.mixins.impropers

# -*- coding: utf-8 -*-
"""
=======================================================================================
Class representations of improper angles (:mod:`sknano.core.atoms.mixins.impropers`)
=======================================================================================

.. currentmodule:: sknano.core.atoms.mixins.impropers

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

from collections import namedtuple
import numpy as np

# from sknano.core.math import vector as vec
from sknano.core.math import abs_cap
from .bonds import Bond
from .topology_base import AngularTopology, AngularTopologyCollection, \
    TopologyStats, check_operands as check_operands_

__all__ = ['compute_improper', 'Improper', 'Impropers', 'ImproperStats']

ImproperStats = namedtuple('ImproperStats', TopologyStats._fields)


[docs]def compute_improper(*atoms, check_operands=True, degrees=False): """Compute improper angle. Parameters ---------- *atoms : {:class:`~python:list`, :class:`~sknano.core.atoms.Atoms`} :class:`~python:list` of :class:`~sknano.core.atoms.Atom`\ s or an :class:`~sknano.core.atoms.Atoms` object. check_operands : :class:`~python:bool`, optional degrees : :class:`~python:bool`, optional Returns ------- :class:`~python:float` Raises ------ :class:`~python:TypeError` if `atoms` is not a list of :class:`~sknano.core.atoms.Atom` objects or an :class:`~sknano.core.atoms.Atoms` object. :class:`~python:ValueError` if len(atoms) != 4. """ if check_operands: atoms = check_operands_(*atoms, size=4) atom1, atom2, atom3, atom4 = atoms b21 = Bond(atom2, atom1).vector b23 = Bond(atom2, atom3).vector b34 = Bond(atom3, atom4).vector m = b21.cross(b23) n = b23.cross(b34) cosa = -m.dot(n) / (m.norm * n.norm) cosa = abs_cap(cosa, 1) angle = np.arccos(cosa) if degrees: angle = np.degrees(angle) return angle
[docs]class Improper(AngularTopology): """Class representation of improper angle between 4 `Atom` objects. Parameters ---------- *atoms : {:class:`~python:list`, :class:`~sknano.core.atoms.Atoms`} :class:`~python:list` of :class:`~sknano.core.atoms.Atom`\ s or an :class:`~sknano.core.atoms.Atoms` object. size : :class:`~python:int` parent : Parent :class:`~sknano.core.atoms.Molecule`, if any. id : :class:`~python:int` check_operands : :class:`~python:bool`, optional degrees : :class:`~python:bool`, optional Raises ------ :class:`~python:TypeError` if `atoms` is not a list of :class:`~sknano.core.atoms.Atom` objects or an :class:`~sknano.core.atoms.Atoms` object. :class:`~python:ValueError` if len(atoms) != 4. """ def __init__(self, *args, **kwargs): super().__init__(*args, size=4, **kwargs) self.fmtstr = "{atom1!r}, {atom2!r}, {atom3!r}, {atom4!r} " + \ super().fmtstr def __str__(self): """Return nice string representation of `Improper`.""" return "Improper({!r}->{!r}->{!r}->{!r})".format(self.atom1.id, self.atom2.id, self.atom3.id, self.atom4.id) @property def atom1(self): """:class:`~sknano.core.atoms.Atom` 1 in `Improper`.""" return self.atoms[0] @property def atom2(self): """:class:`~sknano.core.atoms.Atom` 2 in `Improper`.""" return self.atoms[1] @property def atom3(self): """:class:`~sknano.core.atoms.Atom` 3 in `Improper`.""" return self.atoms[2] @property def atom4(self): """:class:`~sknano.core.atoms.Atom` 4 in `Improper`.""" return self.atoms[3] @property def improper(self): """An alias for :attr:`Topology.measure`.""" return self.measure
[docs] def compute_measure(self): """Compute the bond angle, which is the measure of an :class:`Angle`. Returns ------- :class:`~python:float` """ return compute_improper(*self.atoms, degrees=self.degrees)
[docs] def todict(self): """Return :class:`~python:dict` of constructor parameters.""" super_dict = super().todict() super_dict.update(dict(atom1=self.atom1, atom2=self.atom2, atom3=self.atom3, atom4=self.atom4)) return super_dict
[docs]class Impropers(AngularTopologyCollection): """Base class for collection of atom `Improper`\ s. Parameters ---------- topolist : {None, sequence, `Impropers`}, optional if not `None`, then a list of `Improper` objects parent : Parent :class:`~sknano.core.atoms.Molecule`, if any. degrees : :class:`~python:bool`, optional """ @property def __item_class__(self): return Improper @property def Nimpropers(self): """Number of `Improper`\ s in `Impropers`.""" return len(self) @property def impropers(self): """:class:`~numpy:numpy.ndarray` of :attr:`~Improper.angles`\ s.""" return self.measures @property def mean_improper(self): """Mean improper angle.""" return self.mean_measure @property def statistics(self): """Improper stats.""" return ImproperStats(**super().statistics._asdict())