Source code for sknano.core.atoms._lattice_atoms
# -*- coding: utf-8 -*-
"""
===============================================================================
Atom classes for crystal lattices (:mod:`sknano.core.atoms._lattice_atoms`)
===============================================================================
.. currentmodule:: sknano.core.atoms._lattice_atoms
"""
from __future__ import absolute_import, division, print_function
from __future__ import unicode_literals
__docformat__ = 'restructuredtext en'
# from operator import attrgetter
from functools import total_ordering
import copy
import numbers
import numpy as np
from sknano.core.math import Vector
from ._atoms import Atom, Atoms
__all__ = ['LatticeAtom', 'LatticeAtoms']
@total_ordering
[docs]class LatticeAtom(Atom):
"""Class representation of a crystal structure lattice atom.
Parameters
----------
lattice : :class:`~sknano.core.crystallography.Crystal3DLattice`
xs, ys, zs : float
"""
def __init__(self, *args, lattice=None, xs=None, ys=None, zs=None,
**kwargs):
super().__init__(*args, **kwargs)
self.lattice = lattice
if all([x is not None for x in (xs, ys, zs)]):
self.rs = Vector([xs, ys, zs])
self.fmtstr = super().fmtstr + \
", lattice={lattice!r}, xs={xs!r}, ys={ys!r}, zs={zs!r}"
# ", lattice={lattice!r}, xs={xs:.6f}, ys={ys:.6f}, zs={zs:.6f}"
def __eq__(self, other):
return self.rs == other.rs and super().__eq__(other)
def __lt__(self, other):
"""Test if `self` is *less than* `other`."""
return (self.rs < other.rs and super().__le__(other)) or \
(self.rs <= other.rs and super().__lt__(other))
def __dir__(self):
attrs = super().__dir__()
attrs.extend(['lattice', 'xs', 'ys', 'zs'])
return attrs
@property
def xs(self):
""":math:`x`-coordinate in units of **Angstroms**.
Returns
-------
float
:math:`x`-coordinate in units of **Angstroms**.
"""
try:
return self.rs.x
except AttributeError:
return None
@xs.setter
def xs(self, value):
"""Set `Atom` :math:`x`-coordinate in units of **Angstroms**.
Parameters
----------
value : float
:math:`x`-coordinate in units of **Angstroms**.
"""
if not isinstance(value, numbers.Number):
raise TypeError('Expected a number')
try:
rs = self.rs
rs.x = value
self._update_cartesian_coordinate(rs)
except AttributeError:
pass
@property
def ys(self):
""":math:`y`-coordinate in units of **Angstroms**.
Returns
-------
float
:math:`y`-coordinate in units of **Angstroms**.
"""
try:
return self.rs.y
except AttributeError:
return None
@ys.setter
def ys(self, value):
"""Set `Atom` :math:`y`-coordinate in units of **Angstroms**.
Parameters
----------
value : float
:math:`y`-coordinate in units of **Angstroms**.
"""
if not isinstance(value, numbers.Number):
raise TypeError('Expected a number')
try:
rs = self.rs
rs.y = value
self._update_cartesian_coordinate(rs)
except AttributeError:
pass
@property
def zs(self):
""":math:`z`-coordinate in units of **Angstroms**.
Returns
-------
float
:math:`z`-coordinate in units of **Angstroms**.
"""
try:
return self.rs.z
except AttributeError:
return None
@zs.setter
def zs(self, value):
"""Set `Atom` :math:`z`-coordinate in units of **Angstroms**.
Parameters
----------
value : float
:math:`z`-coordinate in units of **Angstroms**.
"""
if not isinstance(value, numbers.Number):
raise TypeError('Expected a number')
try:
rs = self.rs
rs.z = value
self._update_cartesian_coordinate(rs)
except AttributeError:
pass
@property
def rs(self):
""":math:`x, y, z` components of `Atom` position vector.
Returns
-------
ndarray
3-element ndarray of [:math:`x, y, z`] coordinates of `Atom`.
"""
try:
return Vector(self.lattice.cartesian_to_fractional(self.r))
except AttributeError:
return None
@rs.setter
def rs(self, value):
"""Set :math:`x, y, z` components of `Atom` position vector.
Parameters
----------
value : array_like
:math:`x, y, z` coordinates of `Atom` position vector relative to
the origin.
"""
if not isinstance(value, (list, np.ndarray)):
raise TypeError('Expected an array_like object')
self._update_cartesian_coordinate(Vector(value, nd=3))
def _update_cartesian_coordinate(self, rs):
try:
self.r = self.lattice.fractional_to_cartesian(rs)
except AttributeError:
pass
@property
def lattice(self):
""":class:`~sknano.core.crystallography.Crystal3DLattice`."""
return self._lattice
@lattice.setter
def lattice(self, value):
self._lattice = copy.deepcopy(value)
try:
self.rs = self.lattice.cartesian_to_fractional(self.r)
except AttributeError:
pass
[docs] def rotate(self, **kwargs):
"""Rotate `Atom` position vector.
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`
"""
try:
self.lattice.rotate(**kwargs)
except AttributeError:
pass
super().rotate(**kwargs)
[docs] def translate(self, t, fix_anchor_point=True):
"""Translate `Atom` position vector by :class:`Vector` `t`.
Parameters
----------
t : :class:`Vector`
fix_anchor_point : bool, optional
"""
if not fix_anchor_point:
try:
self.lattice.translate(t)
except AttributeError:
pass
super().translate(t, fix_anchor_point=fix_anchor_point)
[docs] def todict(self):
super_dict = super().todict()
super_dict.update(dict(lattice=self.lattice, xs=self.xs, ys=self.ys,
zs=self.zs))
return super_dict
[docs]class LatticeAtoms(Atoms):
"""An `Atoms` sub-class for crystal structure lattice atoms.
Sub-class of `Atoms` class, and a container class for lists of
:class:`~sknano.core.atoms.LatticeAtom` instances.
Parameters
----------
atoms : {None, sequence, `LatticeAtoms`}, optional
if not `None`, then a list of `LatticeAtom` instance objects or an
existing `LatticeAtoms` instance object.
"""
@property
def __atom_class__(self):
return LatticeAtom
@property
def rs(self):
""":class:`~numpy:numpy.ndarray` of :attr:`Atom.r` position \
`Vector`\ s"""
return np.asarray([atom.rs for atom in self])
@property
def xs(self):
""":class:`~numpy:numpy.ndarray` of `Atom`\ s :math:`x` coordinates."""
return self.rs[:, 0]
@property
def ys(self):
""":class:`~numpy:numpy.ndarray` of `Atom`\ s :math:`y` coordinates."""
return self.rs[:, 1]
@property
def zs(self):
""":class:`~numpy:numpy.ndarray` of `Atom`\ s :math:`z` coordinates."""
return self.rs[:, 2]
@property
def lattice(self):
try:
return self[0].lattice
except IndexError:
return None
@lattice.setter
def lattice(self, value):
[setattr(atom, 'lattice', value) for atom in self]