Ejemplo n.º 1
0
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)
Ejemplo n.º 2
0
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)