class LatticeBase(BaseClass): """Base class for crystallographic lattice objects. Parameters ---------- nd : int cell_matrix : array_like orientation_matrix : array_like, optional offset : array_like, optional """ def __init__(self, nd=None, cell_matrix=None, orientation_matrix=None, offset=None): super().__init__() self.nd = nd self.offset = Point(offset, nd=3) if cell_matrix is not None and orientation_matrix is None: orientation_matrix = cell_matrix.T * self.fractional_matrix if orientation_matrix is None: orientation_matrix = np.asmatrix(np.identity(3)) self.orientation_matrix = np.asmatrix(orientation_matrix) self.lattice_type = None def __dir__(self): return ['nd', 'offset', 'orientation_matrix'] def __eq__(self, other): if isinstance(other, type(self)): return self is other or \ all([np.allclose(getattr(self, attr), getattr(other, attr)) for attr in dir(self)]) def __lt__(self, other): if isinstance(other, type(self)): try: return self.cell_volume < other.cell_volume except AttributeError: return self.cell_area < other.cell_area @property def cell_matrix(self): """Matrix of lattice row vectors. Same as :attr:`Crystal2DLattice.ortho_matrix`\ .T or :attr:`Crystal3DLattice.ortho_matrix`\ .T. """ return (self.orientation_matrix * self.ortho_matrix).T @property def matrix(self): """Alias for \ :attr:`~sknano.core.crystallography.LatticeBase.cell_matrix`.""" return self.cell_matrix @property def fractional_matrix(self): """Transformation matrix to convert from cartesian coordinates to \ fractional coordinates.""" return np.linalg.inv(self.ortho_matrix) @property def metric_tensor(self): """Metric tensor.""" return self.cell_matrix * self.cell_matrix.T def fractional_to_cartesian(self, fcoords): """Convert fractional coordinate to cartesian coordinate. Parameters ---------- fcoords : array_like Returns ------- :class:`~numpy:numpy.ndarray` """ ccoords = self.orientation_matrix * self.ortho_matrix * \ np.asmatrix(fcoords).T + self.offset.column_matrix try: return ccoords.T.A.reshape((3, )) except ValueError: return ccoords.T.A.reshape((len(fcoords), 3)) def cartesian_to_fractional(self, ccoords): """Convert cartesian coordinate to fractional coordinate. Parameters ---------- ccoords : array_like Returns ------- :class:`~numpy:numpy.ndarray` """ fcoords = np.linalg.inv(self.ortho_matrix) * \ np.linalg.inv(self.orientation_matrix) * \ (np.asmatrix(ccoords).T - self.offset.column_matrix) try: return fcoords.T.A.reshape((3, )) except ValueError: return fcoords.T.A.reshape((len(ccoords), 3)) def wrap_fractional_coordinate(self, p, epsilon=1e-6, pbc=None): """Wrap fractional coordinate to lie within unit cell. Parameters ---------- p : array_like Returns ------- :class:`~numpy:numpy.ndarray` """ if pbc is None: pbc = np.asarray(np.ones(3), dtype=bool) p = np.ma.array(p, mask=~pbc) p = np.ma.fmod(p, 1) p[np.ma.where(p < 0)] += 1 p[np.ma.where(p > 1 - epsilon)] -= 1 p[np.ma.where(np.logical_or((p > 1 - epsilon), (p < epsilon)))] = 0 p.mask = np.ma.nomask return p.tolist() def wrap_cartesian_coordinate(self, p, pbc=None): """Wrap cartesian coordinate to lie within unit cell. Parameters ---------- p : array_like Returns ------- :class:`~numpy:numpy.ndarray` """ return self.fractional_to_cartesian( self.wrap_fractional_coordinate(self.cartesian_to_fractional(p), pbc=pbc)) 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 unit cell. 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` See Also -------- core.math.rotate """ if self.nd == 2: axis = 'z' if transform_matrix is None: transform_matrix = \ np.asmatrix( rotation_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)) # print('transform_matrix: {}'.format(transform_matrix)) # 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) self.orientation_matrix = \ transform_matrix * self.orientation_matrix def translate(self, t): """Translate lattice. Parameters ---------- t : :class:`Vector` See Also -------- core.math.translate """ self.offset.translate(t)
class LatticeBase(BaseClass): """Base class for crystallographic lattice objects. Parameters ---------- nd : int cell_matrix : array_like orientation_matrix : array_like, optional offset : array_like, optional """ def __init__(self, nd=None, cell_matrix=None, orientation_matrix=None, offset=None): super().__init__() self.nd = nd self.offset = Point(offset, nd=3) if cell_matrix is not None and orientation_matrix is None: orientation_matrix = cell_matrix.T * self.fractional_matrix if orientation_matrix is None: orientation_matrix = np.asmatrix(np.identity(3)) self.orientation_matrix = np.asmatrix(orientation_matrix) self.lattice_type = None def __dir__(self): return ['nd', 'offset', 'orientation_matrix'] def __eq__(self, other): if isinstance(other, type(self)): return self is other or \ all([np.allclose(getattr(self, attr), getattr(other, attr)) for attr in dir(self)]) def __lt__(self, other): if isinstance(other, type(self)): try: return self.cell_volume < other.cell_volume except AttributeError: return self.cell_area < other.cell_area @property def cell_matrix(self): """Matrix of lattice row vectors. Same as :attr:`Crystal2DLattice.ortho_matrix`\ .T or :attr:`Crystal3DLattice.ortho_matrix`\ .T. """ return (self.orientation_matrix * self.ortho_matrix).T @property def matrix(self): """Alias for \ :attr:`~sknano.core.crystallography.LatticeBase.cell_matrix`.""" return self.cell_matrix @property def fractional_matrix(self): """Transformation matrix to convert from cartesian coordinates to \ fractional coordinates.""" return np.linalg.inv(self.ortho_matrix) @property def metric_tensor(self): """Metric tensor.""" return self.cell_matrix * self.cell_matrix.T def fractional_to_cartesian(self, fcoords): """Convert fractional coordinate to cartesian coordinate. Parameters ---------- fcoords : array_like Returns ------- :class:`~numpy:numpy.ndarray` """ ccoords = self.orientation_matrix * self.ortho_matrix * \ np.asmatrix(fcoords).T + self.offset.column_matrix try: return ccoords.T.A.reshape((3, )) except ValueError: return ccoords.T.A.reshape((len(fcoords), 3)) def cartesian_to_fractional(self, ccoords): """Convert cartesian coordinate to fractional coordinate. Parameters ---------- ccoords : array_like Returns ------- :class:`~numpy:numpy.ndarray` """ fcoords = np.linalg.inv(self.ortho_matrix) * \ np.linalg.inv(self.orientation_matrix) * \ (np.asmatrix(ccoords).T - self.offset.column_matrix) try: return fcoords.T.A.reshape((3, )) except ValueError: return fcoords.T.A.reshape((len(ccoords), 3)) def wrap_fractional_coordinate(self, p, epsilon=1e-6, pbc=None): """Wrap fractional coordinate to lie within unit cell. Parameters ---------- p : array_like Returns ------- :class:`~numpy:numpy.ndarray` """ if pbc is None: pbc = np.asarray(np.ones(3), dtype=bool) p = np.ma.array(p, mask=~pbc) p = np.ma.fmod(p, 1) p[np.ma.where(p < 0)] += 1 p[np.ma.where(p > 1 - epsilon)] -= 1 p[np.ma.where(np.logical_or((p > 1 - epsilon), (p < epsilon)))] = 0 p.mask = np.ma.nomask return p.tolist() def wrap_cartesian_coordinate(self, p, pbc=None): """Wrap cartesian coordinate to lie within unit cell. Parameters ---------- p : array_like Returns ------- :class:`~numpy:numpy.ndarray` """ return self.fractional_to_cartesian( self.wrap_fractional_coordinate(self.cartesian_to_fractional(p), pbc=pbc)) 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 unit cell. 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` See Also -------- core.math.rotate """ if self.nd == 2: axis = 'z' if transform_matrix is None: transform_matrix = \ np.asmatrix( rotation_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)) # print('transform_matrix: {}'.format(transform_matrix)) # 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) self.orientation_matrix = \ transform_matrix * self.orientation_matrix def translate(self, t): """Translate lattice. Parameters ---------- t : :class:`Vector` See Also -------- core.math.translate """ self.offset.translate(t)