Source code for sknano.core.atoms._poav_atom

# -*- coding: utf-8 -*-
"""
===============================================================================
Atom sub-class for POAV analysis (:mod:`sknano.core.atoms._poav_atom`)
===============================================================================

An `Atom` sub-class for POAV analysis.

.. currentmodule:: sknano.core.atoms._poav_atom

"""
from __future__ import absolute_import, division, print_function
from six.moves import zip
__docformat__ = 'restructuredtext en'

import functools
import operator
import warnings

import numpy as np
np.seterr(all='warn')

from sknano.core.math import vector as vec

from ._kdtree_atom import KDTAtom

__all__ = ['POAV', 'POAV1', 'POAV2', 'POAVR', 'POAVAtomMixin', 'POAVAtom']


[docs]class POAV(object): """Base class for POAV analysis. Parameters ---------- sigma_bonds : `~sknano.core.atoms.Bonds` `~sknano.core.atoms.Bonds` instance. """ def __init__(self, sigma_bonds): self.bonds = sigma_bonds self.bond1 = self.bonds[0].vector self.bond2 = self.bonds[1].vector self.bond3 = self.bonds[2].vector self.bond_angles = self.bonds.angles self.bond_angle_pairs = self.bonds.bond_angle_pairs self.sigma_bond_angle12 = self.bond_angles[0] self.sigma_bond_angle23 = self.bond_angles[1] self.sigma_bond_angle31 = self.bond_angles[2] self.cosa12 = np.cos(self.bond_angles[0]) self.cosa23 = np.cos(self.bond_angles[1]) self.cosa31 = np.cos(self.bond_angles[2]) self._v1 = self.bond1 self._v2 = self.bond2 self._v3 = self.bond3 self._pyramidalization_angles = None self._sigma_pi_angles = None self._misalignment_angles = None @property def v1(self): """:class:`~sknano.core.math.Vector` :math:`\\mathbf{v}_1` \ directed along the :math:`\\sigma`-orbital to the \ nearest-neighbor :class:`~sknano.core.atoms.Atom` \ in :class:`~sknano.core.atoms.Bond` 1.""" return self._v1 @property def v2(self): """:class:`~sknano.core.math.Vector` :math:`\\mathbf{v}_2` \ directed along the :math:`\\sigma`-orbital to the \ nearest-neighbor :class:`~sknano.core.atoms.Atom` \ in :class:`~sknano.core.atoms.Bond` 2.""" return self._v2 @property def v3(self): """:class:`~sknano.core.math.Vector` :math:`\\mathbf{v}_3` \ directed along the :math:`\\sigma`-orbital to the \ nearest-neighbor :class:`~sknano.core.atoms.Atom` \ in :class:`~sknano.core.atoms.Bond` 3.""" return self._v3 @property def Vv1v2v3(self): """Volume of the parallelepiped defined by \ :class:`~sknano.core.math.Vector`\ s `v1`, `v2`, and `v3`. Computes the scalar triple product of vectors :math:`\\mathbf{v}_1`, :math:`\\mathbf{v}_2`, and :math:`\\mathbf{v}_3`: .. math:: V_{v_1v_2v_3} = |\\mathbf{v}_1\\cdot(\\mathbf{v}_2\\times\\mathbf{v}_3)| """ return np.abs(vec.scalar_triple_product(self.v1, self.v2, self.v3)) @property def vpi(self): """General :math:`\\pi`-orbital axis vector \ (:math:`\\mathbf{v}_{\\pi}`) formed by the \ terminii of :class:`~sknano.core.math.Vector`\ s \ :class:`~sknano.core.math.Vector`\ s `v1`, `v2`, and `v3`. .. math:: \\mathbf{v}_{\\pi} = \\mathbf{v}_1 + \\mathbf{v}_2\\ + \\mathbf{v}_3 """ return self.reciprocal_v1 + self.reciprocal_v2 + self.reciprocal_v3 @property def Vpi(self): """:math:`\\mathbf{v}_{\\pi}` unit :class:`~sknano.core.math.Vector` Returns the :math:`\\pi`-orbital axis vector (:math:`\\mathbf{v}_{\\pi}`) unit vector. .. math:: \\mathbf{V}_{\\pi} = \\frac{\\mathbf{v}_{\\pi}}{|\\mathbf{v}_{\\pi}|} """ return self.vpi.unit_vector @property def reciprocal_v1(self): """Reciprocal :class:`~sknano.core.math.Vector` \ :math:`\\mathbf{v}_1^{*}`. Defined as: .. math:: \\mathbf{v}_1^{*} = \\frac{\\mathbf{v}_2\\times\\mathbf{v}_3} {|\\mathbf{v}_1\\cdot(\\mathbf{v}_2\\times\\mathbf{v}_3)|} """ with warnings.catch_warnings(): warnings.filterwarnings('error') try: return vec.cross(self.v2, self.v3) / self.Vv1v2v3 except Warning: return vec.cross(self.v2, self.v3) @property def reciprocal_v2(self): """Reciprocal :class:`~sknano.core.math.Vector` \ :math:`\\mathbf{v}_2^{*}`. Defined as: .. math:: \\mathbf{v}_2^{*} = \\frac{\\mathbf{v}_3\\times\\mathbf{v}_1} {|\\mathbf{v}_1\\cdot(\\mathbf{v}_2\\times\\mathbf{v}_3)|} """ with warnings.catch_warnings(): warnings.filterwarnings('error') try: return vec.cross(self.v3, self.v1) / self.Vv1v2v3 except Warning: return vec.cross(self.v3, self.v1) @property def reciprocal_v3(self): """Reciprocal :class:`~sknano.core.math.Vector` \ :math:`\\mathbf{v}_3^{*}`. Defined as: .. math:: \\mathbf{v}_3^{*} = \\frac{\\mathbf{v}_1\\times\\mathbf{v}_2} {|\\mathbf{v}_1\\cdot(\\mathbf{v}_2\\times\\mathbf{v}_3)|} """ with warnings.catch_warnings(): warnings.filterwarnings('error') try: return vec.cross(self.v1, self.v2) / self.Vv1v2v3 except Warning: return vec.cross(self.v1, self.v2) @property def V1(self): """:math:`\\mathbf{v}_1` unit :class:`~sknano.core.math.Vector` .. math:: \\mathbf{V}_1\\equiv\\frac{\\mathbf{v}_1}{|\\mathbf{v}_1|} """ return self.bond1.unit_vector @property def V2(self): """:math:`\\mathbf{v}_2` unit :class:`~sknano.core.math.Vector` .. math:: \\mathbf{V}_2\\equiv\\frac{\\mathbf{v}_2}{|\\mathbf{v}_2|} """ return self.bond2.unit_vector @property def V3(self): """:math:`\\mathbf{v}_3` unit :class:`~sknano.core.math.Vector` .. math:: \\mathbf{V}_3\\equiv\\frac{\\mathbf{v}_3}{|\\mathbf{v}_3|} """ return self.bond3.unit_vector @property def R1(self): """:class:`~sknano.core.atoms.Bond` 1 \ :class:`~sknano.core.math.Vector` \ :attr:`~sknano.core.math.Vector.length`. """ return self.bond1.length @property def R2(self): """:class:`~sknano.core.atoms.Bond` 2 \ :class:`~sknano.core.math.Vector` \ :attr:`~sknano.core.math.Vector.length`. """ return self.bond2.length @property def R3(self): """:class:`~sknano.core.atoms.Bond` 3 \ :class:`~sknano.core.math.Vector` \ :attr:`~sknano.core.math.Vector.length`. """ return self.bond3.length @property def t(self): """:math:`\\frac{1}{6}` the volume of the tetrahedron defined by \ :class:`~sknano.core.math.Vector`\ s `v1`, `v2`, and `v3`. .. math:: t = \\frac{|\\mathbf{v}_1\\cdot(\\mathbf{v}_2\\times\\mathbf{v}_3)|}{6} """ return self.Vv1v2v3 / 6 @property def T(self): """:math:`\\frac{1}{6}` the volume of the tetrahedron defined by \ :class:`~sknano.core.math.Vector`\ s `V1`, `V2`, and `V3`. .. math:: T = \\frac{|\\mathbf{V}_1\\cdot(\\mathbf{V}_2\\times\\mathbf{V}_3)|}{6} """ return np.abs(vec.scalar_triple_product(self.V1, self.V2, self.V3) / 6) @property def A(self): """Magnitude of :math:`\\mathbf{v}_{\\pi}`.""" return self.vpi.magnitude @property def H(self): """Altitude of tetrahedron.""" return 3 * self.T / self.A @property def sigma_pi_angles(self): """List of :math:`\\theta_{\\sigma-\\pi}` angles.""" return self._sigma_pi_angles @sigma_pi_angles.setter def sigma_pi_angles(self, value): """Set list of :math:`\\theta_{\\sigma-\\pi}` angles.""" if not isinstance(value, list): raise TypeError('Expected a list') self._sigma_pi_angles = value @property def pyramidalization_angles(self): """List of pyramidalization :math:`\\theta_{P}` angles.""" return self._pyramidalization_angles @pyramidalization_angles.setter def pyramidalization_angles(self, value): """Set list of :math:`\\theta_{P}` angles.""" if not isinstance(value, list): raise TypeError('Expected a list') self._pyramidalization_angles = value @property def misalignment_angles(self): """List of misalignment :math:`\\phi_{i}` angles.""" return self._misalignment_angles @misalignment_angles.setter def misalignment_angles(self, value): """Set list of :math:`\\phi` angles.""" if not isinstance(value, list): raise TypeError('Expected a list') self._misalignment_angles = value
[docs] def todict(self, rad2deg=False): """Return dictionary of `POAV` class attributes.""" sigma_pi_angles = self.sigma_pi_angles pyramidalization_angles = self.pyramidalization_angles misalignment_angles = self.misalignment_angles if rad2deg: sigma_pi_angles = np.degrees(sigma_pi_angles) pyramidalization_angles = np.degrees(pyramidalization_angles) misalignment_angles = np.degrees(misalignment_angles) return dict(bond1=self.bond1.length, bond2=self.bond2.length, bond3=self.bond3.length, sigma_pi_angle1=sigma_pi_angles[0], sigma_pi_angle2=sigma_pi_angles[1], sigma_pi_angle3=sigma_pi_angles[2], pyramidalization_angle1=pyramidalization_angles[0], pyramidalization_angle2=pyramidalization_angles[1], pyramidalization_angle3=pyramidalization_angles[2], misalignment_angle1=misalignment_angles[0], misalignment_angle2=misalignment_angles[1], misalignment_angle3=misalignment_angles[2], T=self.T, H=self.H, A=self.A)
[docs]class POAV1(POAV): """:class:`POAV` sub-class for POAV1 analysis.""" def __init__(self, *args): super(POAV1, self).__init__(*args) self._v1 = self.V1 self._v2 = self.V2 self._v3 = self.V3 @property def m(self): """:math:`s` character content of the :math:`\\pi`-orbital \ (:math:`s^mp`) for :math:`sp^3` normalized hybridization.""" cos2sigmapi = np.cos(np.mean(self.sigma_pi_angles))**2 return 2 * cos2sigmapi / (1 - 3 * cos2sigmapi) @property def n(self): """:math:`p` character content of the :math:`\\sigma`-orbitals \ (:math:`sp^n`) for :math:`sp^3` normalized hybridization.""" return 3 * self.m + 2
[docs] def todict(self, rad2deg=False): """Return dictionary of `POAV1` class attributes.""" super_dict = super(POAV1, self).todict(rad2deg=rad2deg) super_dict.update(dict(m=self.m, n=self.n)) return super_dict
[docs]class POAV2(POAV): """:class:`POAV` sub-class for POAV2 analysis.""" def __init__(self, *args): super(POAV2, self).__init__(*args) vi = [] for bond, pair in zip(self.bonds, self.bond_angle_pairs): cosa = \ np.cos(self.bond_angles[ np.in1d(self.bonds, pair, invert=True)]) vi.append(cosa * bond.vector.unit_vector) self._v1 = vi[0] self._v2 = vi[1] self._v3 = vi[2] @property def T(self): """:math:`\\frac{1}{6}` the volume of the tetrahedron defined by \ :class:`~sknano.core.math.Vector`\ s `V1`, `V2`, and `V3`. .. math:: T = \\cos\\theta_{12}\\cos\\theta_{23}\\cos\\theta_{31}\\times \\frac{|\\mathbf{V}_1\\cdot(\\mathbf{V}_2\\times\\mathbf{V}_3)|}{6} """ return -functools.reduce(operator.mul, np.cos(self.bonds.angles), 1) * \ super(POAV2, self).T @property def n1(self): """:math:`p` character content of the :math:`\\sigma`-orbital \ hybridization for :math:`\\sigma_1` bond.""" return -self.cosa23 / (self.cosa12 * self.cosa31) @property def n2(self): """:math:`p` character content of the :math:`\\sigma`-orbital \ hybridization for :math:`\\sigma_2` bond.""" return -self.cosa31 / (self.cosa12 * self.cosa23) @property def n3(self): """:math:`p` character content of the :math:`\\sigma`-orbital \ hybridization for :math:`\\sigma_3` bond.""" return -self.cosa12 / (self.cosa31 * self.cosa23) @property def m(self): """:math:`s` character content of the :math:`\\pi`-orbital \ (:math:`s^mp`) for :math:`sp^3` normalized hybridization.""" s1 = 1 / (1 + self.n1) s2 = 1 / (1 + self.n2) s3 = 1 / (1 + self.n3) return 1 / sum([s1, s2, s3]) - 1
[docs] def todict(self, rad2deg=False): """Return dictionary of `POAV2` class attributes.""" super_dict = super(POAV2, self).todict(rad2deg=rad2deg) super_dict.update(dict(m=self.m, n1=self.n1, n2=self.n2, n3=self.n3)) return super_dict
[docs]class POAVR(POAV): """:class:`POAV` sub-class for POAVR analysis.""" def __init__(self, *args): super(POAVR, self).__init__(*args) vi = [] for R, V in zip([self.R1, self.R2, self.R3], [self.V1, self.V2, self.V3]): vi.append(R * V) self._v1 = vi[0] self._v2 = vi[1] self._v3 = vi[2] @property def T(self): """:math:`\\frac{1}{6}` the volume of the tetrahedron defined by \ :class:`~sknano.core.math.Vector`\ s `V1`, `V2`, and `V3`. .. math:: T = R_1 R_2 R_3 \\times \\frac{|\\mathbf{V}_1\\cdot(\\mathbf{V}_2\\times\\mathbf{V}_3)|}{6} """ return self.R1 * self.R2 * self.R3 * super(POAVR, self).T
[docs]class POAVAtomMixin(object): """Mixin class for `POAVAtom`.""" @property def POAV1(self): """:class:`~sknano.utils.analysis.POAV1` instance.""" return self._POAV1 @POAV1.setter def POAV1(self, value): """Set :class:`~sknano.utils.analysis.POAV1` instance.""" if not isinstance(value, POAV1): raise TypeError('Expected a `POAV1` instance.') self._POAV1 = value @property def POAV2(self): """:class:`~sknano.utils.analysis.POAV2` instance.""" return self._POAV2 @POAV2.setter def POAV2(self, value): """Set :class:`~sknano.utils.analysis.POAV2` instance.""" if not isinstance(value, POAV2): raise TypeError('Expected a `POAV2` instance.') self._POAV2 = value @property def POAVR(self): """:class:`~sknano.utils.analysis.POAVR` instance.""" return self._POAVR @POAVR.setter def POAVR(self, value): """Set :class:`~sknano.utils.analysis.POAVR` instance.""" if not isinstance(value, POAVR): raise TypeError('Expected a `POAVR` instance.') self._POAVR = value
[docs]class POAVAtom(POAVAtomMixin, KDTAtom): """An `Atom` sub-class for POAV analysis.""" _atomattrs = KDTAtom._atomattrs + ['POAV1', 'POAV2', 'POAVR'] def __init__(self, **kwargs): super(POAVAtom, self).__init__(**kwargs) self._POAV1 = None self._POAV2 = None self._POAVR = None