Ejemplo n.º 1
0
    def __init__(self,
                 lattice=None,
                 basis=None,
                 coords=None,
                 cartesian=False,
                 wrap_coords=False):

        super().__init__()

        if basis is None:
            basis = BasisAtoms()
        else:
            basis = BasisAtoms(basis)
            basis.lattice = lattice
            if lattice is not None and coords is not None:
                for atom, pos in zip(basis, coords):
                    atom.lattice = lattice
                    if not cartesian:
                        atom.rs = pos
                    else:
                        atom.rs = lattice.cartesian_to_fractional(pos)

        self.lattice = lattice
        self.basis = basis
        self.wrap_coords = wrap_coords
        self.fmtstr = "{lattice!r}, {basis!r}, {coords!r}, " + \
            "cartesian=False, wrap_coords={wrap_coords!r}"
Ejemplo n.º 2
0
 def from_pymatgen_structure(cls, structure):
     atoms = BasisAtoms()
     for site in structure.sites:
         atoms.append(BasisAtom(element=site.specie.symbol,
                                x=site.x, y=site.y, z=site.z))
     return cls(lattice=Crystal2DLattice(
                cell_matrix=structure.lattice.matrix), basis=atoms)
Ejemplo n.º 3
0
    def __init__(self, lattice=None, basis=None, coords=None, cartesian=False,
                 wrap_coords=False, unit_cell=None, scaling_matrix=None):
        super().__init__()

        if unit_cell is None and basis is not None:
            basis = BasisAtoms(basis)
            basis.lattice = lattice
            if lattice is not None and coords is not None:
                for atom, pos in zip(basis, coords):
                    atom.lattice = lattice
                    if not cartesian:
                        atom.rs = pos
                    else:
                        atom.rs = lattice.cartesian_to_fractional(pos)

        # if basis is None:
        #     basis = BasisAtoms()

        # These attributes may be reset in the `@scaling_matrix.setter`
        # method and so they need to be initialized *before* setting
        # `self.scaling_matrix`.
        self.basis = basis
        self.lattice = lattice

        self.unit_cell = unit_cell
        self.wrap_coords = wrap_coords
        self.scaling_matrix = scaling_matrix

        self.fmtstr = \
            "lattice={lattice!r}, basis={basis!r}, coords={coords!r}, " + \
            "cartesian=False, wrap_coords={wrap_coords!r}, " + \
            "unit_cell={unit_cell!r}, scaling_matrix={scaling_matrix!r}"
Ejemplo n.º 4
0
def test4():
    lattice = Crystal3DLattice.cubic(a=5.0)
    basis = BasisAtoms(atoms=['C', 'C'])
    print(basis)
    print(basis.lattice)
    assert_true(basis.lattice is None)
    basis.lattice = lattice
    assert_true(isinstance(basis.lattice, Crystal3DLattice))
Ejemplo n.º 5
0
    def generate_unit_cell(self):
        """Generate the nanotube unit cell."""
        eps = 0.01

        e1 = self.element1
        e2 = self.element2
        N = self.N
        T = self.T
        rt = self.rt

        psi, tau, dpsi, dtau = self.unit_cell_symmetry_params

        a = compute_Ch(self.n, self.m, bond=self.bond)
        b = self.layer_spacing
        c = compute_T(self.n, self.m, bond=self.bond, length=True)
        lattice = Crystal3DLattice.orthorhombic(a, b, c)

        basis = Atoms()
        if self.verbose:
            print('dpsi: {}'.format(dpsi))
            print('dtau: {}\n'.format(dtau))

        for i in range(N):
            for j, element in enumerate((e1, e2), start=1):
                theta = i * psi
                h = i * tau

                if j == 2:
                    theta += dpsi
                    h -= dtau

                x = rt * theta
                z = h

                while z > T - eps:
                    z -= T

                if z < 0:
                    z += T

                xs, ys, zs = \
                    lattice.cartesian_to_fractional([x, 0, z])
                if self.wrap_coords:
                    xs, ys, zs = \
                        lattice.wrap_fractional_coordinate([xs, ys, zs])

                if self.debug:
                    print('i={}: x, z = ({:.6f}, {:.6f})'.format(i, x, z))

                atom = Atom(element, lattice=lattice, xs=xs, ys=ys, zs=zs)
                atom.rezero()

                if self.verbose:
                    print('Basis Atom:\n{}'.format(atom))

                basis.append(atom)

        self.unit_cell = UnitCell(lattice=lattice, basis=basis)
Ejemplo n.º 6
0
    def scaling_matrix(self, value):
        if value is None:
            self._scaling_matrix = np.asmatrix(np.ones(3, dtype=int))
            return

        if not isinstance(value, (int, float, tuple, list, np.ndarray)):
            return

        if isinstance(value, np.ndarray) and \
                ((value.shape == np.ones(3).shape and
                  np.allclose(value, np.ones(3))) or
                 (value.shape == np.eye(3).shape and
                  np.allclose(value, np.eye(3)))):
            self._scaling_matrix = np.asmatrix(value)
            return

        if isinstance(value, numbers.Number):
            value = self.lattice.nd * [int(value)]

        scaling_matrix = np.asmatrix(value, dtype=int)
        # scaling_matrix = np.asmatrix(value)
        if scaling_matrix.shape != self.lattice.matrix.shape:
            scaling_matrix = np.diagflat(scaling_matrix)
        self._scaling_matrix = scaling_matrix

        self.lattice = self.lattice.__class__(cell_matrix=self.scaling_matrix *
                                              self.lattice.matrix)

        tvecs = \
            np.asarray(
                np.asmatrix(supercell_lattice_points(self.scaling_matrix)) *
                self.lattice.matrix)

        basis = self.basis[:]
        self.basis = BasisAtoms()
        for atom in basis:
            for tvec in tvecs:
                xs, ys, zs = \
                    self.lattice.cartesian_to_fractional(atom.r + tvec)
                if self.wrap_coords:
                    xs, ys, zs = \
                        self.lattice.wrap_fractional_coordinate(
                            [xs, ys, zs])
                self.basis.append(
                    BasisAtom(atom.element,
                              lattice=self.lattice,
                              xs=xs,
                              ys=ys,
                              zs=zs))
Ejemplo n.º 7
0
    def __init__(self, a=lattparams['alpha_quartz']['a'],
                 c=lattparams['alpha_quartz']['c'], **kwargs):
        lattice = Crystal3DLattice.hexagonal(a, c)
        basis = BasisAtoms(3 * ["Si"] + 6 * ["O"])
        coords = [[0.4697, 0.0000, 0.0000],
                  [0.0000, 0.4697, 0.6667],
                  [0.5305, 0.5303, 0.3333],
                  [0.4133, 0.2672, 0.1188],
                  [0.2672, 0.4133, 0.5479],
                  [0.7328, 0.1461, 0.7855],
                  [0.5867, 0.8539, 0.2145],
                  [0.8539, 0.5867, 0.4521],
                  [0.1461, 0.7328, 0.8812]]
        alpha_quartz = pymatgen_structure(lattice.cell_matrix,
                                          basis.symbols, coords)
        alpha_quartz = \
            Crystal3DStructure.from_pymatgen_structure(alpha_quartz)

        # alpha_quartz = \
        #     HexagonalStructure.from_spacegroup(154, a, c, ["Si", "O"],
        #                                        [[0.4697, 0.0000, 0.0000],
        #                                         [0.4135, 0.2669, 0.1191]],
        #                                        scaling_matrix=scaling_matrix)

        super().__init__(structure=alpha_quartz, **kwargs)
Ejemplo n.º 8
0
    def from_pymatgen_structure(cls, structure):
        """Return a `Crystal3DStructure` from a \
            :class:`pymatgen:pymatgen.core.Structure`.

        Parameters
        ----------
        structure : :class:`pymatgen:pymatgen.core.Structure`

        Returns
        -------
        :class:`Crystal3DStructure`

        """
        atoms = BasisAtoms()
        for site in structure.sites:
            atoms.append(BasisAtom(site.specie.symbol,
                                   x=site.x, y=site.y, z=site.z))
        return cls(lattice=Crystal3DLattice(
                   cell_matrix=structure.lattice.matrix), basis=atoms)
Ejemplo n.º 9
0
    def __init__(self,
                 lattice=None,
                 basis=None,
                 coords=None,
                 cartesian=False,
                 wrap_coords=False,
                 unit_cell=None,
                 scaling_matrix=None):
        super().__init__()

        if unit_cell is None and basis is not None:
            basis = BasisAtoms(basis)
            basis.lattice = lattice
            if lattice is not None and coords is not None:
                for atom, pos in zip(basis, coords):
                    atom.lattice = lattice
                    if not cartesian:
                        atom.rs = pos
                    else:
                        atom.rs = lattice.cartesian_to_fractional(pos)

        # if basis is None:
        #     basis = BasisAtoms()

        # These attributes may be reset in the `@scaling_matrix.setter`
        # method and so they need to be initialized *before* setting
        # `self.scaling_matrix`.
        self.basis = basis
        self.lattice = lattice

        self.unit_cell = unit_cell
        self.wrap_coords = wrap_coords
        self.scaling_matrix = scaling_matrix

        self.fmtstr = \
            "lattice={lattice!r}, basis={basis!r}, coords={coords!r}, " + \
            "cartesian=False, wrap_coords={wrap_coords!r}, " + \
            "unit_cell={unit_cell!r}, scaling_matrix={scaling_matrix!r}"
Ejemplo n.º 10
0
    def __init__(self, lattice=None, basis=None, coords=None, cartesian=False,
                 wrap_coords=False):

        super().__init__()

        if basis is None:
            basis = BasisAtoms()
        else:
            basis = BasisAtoms(basis)
            basis.lattice = lattice
            if lattice is not None and coords is not None:
                for atom, pos in zip(basis, coords):
                    atom.lattice = lattice
                    if not cartesian:
                        atom.rs = pos
                    else:
                        atom.rs = lattice.cartesian_to_fractional(pos)

        self.lattice = lattice
        self.basis = basis
        self.wrap_coords = wrap_coords
        self.fmtstr = "{lattice!r}, {basis!r}, {coords!r}, " + \
            "cartesian=False, wrap_coords={wrap_coords!r}"
Ejemplo n.º 11
0
def test5():
    atoms = \
        BasisAtoms(atoms=generate_atoms(elements='periodic_table').symbols)
    print(atoms[:5])

    atoms_slice = atoms[5:10]

    atoms[:5] = atoms_slice
    assert_equal(atoms[:5], atoms[5:10])
    print(atoms[:5])

    atoms[:5] = ['C', 'H', 'N', 'Ar', 'He']
    print(atoms[:8])

    atoms[0] = 'Au'
    print(atoms[:2])
Ejemplo n.º 12
0
    def scaling_matrix(self, value):
        if value is None:
            self._scaling_matrix = np.asmatrix(np.ones(3, dtype=int))
            return

        if not isinstance(value, (int, float, tuple, list, np.ndarray)):
            return

        if isinstance(value, np.ndarray) and \
                ((value.shape == np.ones(3).shape and
                  np.allclose(value, np.ones(3))) or
                 (value.shape == np.eye(3).shape and
                  np.allclose(value, np.eye(3)))):
            self._scaling_matrix = np.asmatrix(value)
            return

        if isinstance(value, numbers.Number):
            value = self.lattice.nd * [int(value)]

        scaling_matrix = np.asmatrix(value, dtype=int)
        # scaling_matrix = np.asmatrix(value)
        if scaling_matrix.shape != self.lattice.matrix.shape:
            scaling_matrix = np.diagflat(scaling_matrix)
        self._scaling_matrix = scaling_matrix

        self.lattice = self.lattice.__class__(
            cell_matrix=self.scaling_matrix * self.lattice.matrix)

        tvecs = \
            np.asarray(
                np.asmatrix(supercell_lattice_points(self.scaling_matrix)) *
                self.lattice.matrix)

        basis = self.basis[:]
        self.basis = BasisAtoms()
        for atom in basis:
            for tvec in tvecs:
                xs, ys, zs = \
                    self.lattice.cartesian_to_fractional(atom.r + tvec)
                if self.wrap_coords:
                    xs, ys, zs = \
                        self.lattice.wrap_fractional_coordinate(
                            [xs, ys, zs])
                self.basis.append(BasisAtom(atom.element, lattice=self.lattice,
                                            xs=xs, ys=ys, zs=zs))
Ejemplo n.º 13
0
class CrystalCell(BaseClass):
    """Class representation of crystal structure cell.

    Parameters
    ----------
    lattice : :class:`~sknano.core.crystallography.LatticeBase` sub-class
    basis : {:class:`~python:list`, :class:`~sknano.core.atoms.BasisAtoms`}
    coords : {:class:`~python:list`}, optional
    cartesian : {:class:`~python:bool`}, optional
    wrap_coords : {:class:`~python:bool`}, optional
    unit_cell : :class:`~sknano.core.crystallography.UnitCell`
    scaling_matrix : {:class:`~python:int`, :class:`~python:list`}

    """
    def __init__(self,
                 lattice=None,
                 basis=None,
                 coords=None,
                 cartesian=False,
                 wrap_coords=False,
                 unit_cell=None,
                 scaling_matrix=None):
        super().__init__()

        if unit_cell is None and basis is not None:
            basis = BasisAtoms(basis)
            basis.lattice = lattice
            if lattice is not None and coords is not None:
                for atom, pos in zip(basis, coords):
                    atom.lattice = lattice
                    if not cartesian:
                        atom.rs = pos
                    else:
                        atom.rs = lattice.cartesian_to_fractional(pos)

        # if basis is None:
        #     basis = BasisAtoms()

        # These attributes may be reset in the `@scaling_matrix.setter`
        # method and so they need to be initialized *before* setting
        # `self.scaling_matrix`.
        self.basis = basis
        self.lattice = lattice

        self.unit_cell = unit_cell
        self.wrap_coords = wrap_coords
        self.scaling_matrix = scaling_matrix

        self.fmtstr = \
            "lattice={lattice!r}, basis={basis!r}, coords={coords!r}, " + \
            "cartesian=False, wrap_coords={wrap_coords!r}, " + \
            "unit_cell={unit_cell!r}, scaling_matrix={scaling_matrix!r}"

    def __dir__(self):
        return ['lattice', 'basis', 'unit_cell', 'scaling_matrix']

    def __eq__(self, other):
        if all([attr is not None for attr in
                (self.scaling_matrix, self.unit_cell,
                 other.scaling_matrix, other.unit_cell)]) and \
                self.scaling_matrix.shape == other.scaling_matrix.shape:
            return self is other or \
                (self.unit_cell == other.unit_cell and
                 np.allclose(self.scaling_matrix, other.scaling_matrix))
        elif all(
            [cell is not None for cell in (self.unit_cell, other.unit_cell)]):
            return self is other or \
                (self.unit_cell == other.unit_cell and
                 all([mat is None for mat in
                      (self.scaling_matrix, other.scaling_matrix)]))

    def __lt__(self, other):
        return (self.unit_cell < other.unit_cell and
                self.scaling_matrix <= other.scaling_matrix) \
            or (self.unit_cell <= other.unit_cell and
                self.scaling_matrix < other.scaling_matrix)

    def __iter__(self):
        return iter(self.basis)

    def __getattr__(self, name):
        if name != 'lattice' and self.lattice is not None:
            try:
                return getattr(self.lattice, name)
            except AttributeError:
                pass
        if name != 'basis' and self.basis.Natoms != 0:
            try:
                return getattr(self.basis, name)
            except AttributeError:
                pass
        try:
            return getattr(self.unit_cell, name)
        except AttributeError:
            return super().__getattr__(name)

    @property
    def basis(self):
        return self._basis

    @basis.setter
    def basis(self, value):
        self._basis = value
        # if self.unit_cell is not None:
        #     self.unit_cell.basis[:] = \
        #         self.basis[:self.unit_cell.basis.Natoms]

    @property
    def lattice(self):
        return self._lattice

    @lattice.setter
    def lattice(self, value):
        self._lattice = value
        if self.basis is not None:
            self.basis.lattice = self.lattice

    @property
    def unit_cell(self):
        return self._unit_cell

    @unit_cell.setter
    def unit_cell(self, value):
        if value is not None and not isinstance(value, UnitCell):
            raise ValueError('Expected a `UnitCell` object')
        self._unit_cell = value
        if value is not None:
            if self.lattice is None:
                self._lattice = self.unit_cell.lattice
            if self.basis is None or self.basis.Natoms == 0:
                self._basis = self.unit_cell.basis

    @property
    def scaling_matrix(self):
        """Scaling matrix."""
        return self._scaling_matrix

    @scaling_matrix.setter
    def scaling_matrix(self, value):
        if value is None:
            self._scaling_matrix = np.asmatrix(np.ones(3, dtype=int))
            return

        if not isinstance(value, (int, float, tuple, list, np.ndarray)):
            return

        if isinstance(value, np.ndarray) and \
                ((value.shape == np.ones(3).shape and
                  np.allclose(value, np.ones(3))) or
                 (value.shape == np.eye(3).shape and
                  np.allclose(value, np.eye(3)))):
            self._scaling_matrix = np.asmatrix(value)
            return

        if isinstance(value, numbers.Number):
            value = self.lattice.nd * [int(value)]

        scaling_matrix = np.asmatrix(value, dtype=int)
        # scaling_matrix = np.asmatrix(value)
        if scaling_matrix.shape != self.lattice.matrix.shape:
            scaling_matrix = np.diagflat(scaling_matrix)
        self._scaling_matrix = scaling_matrix

        self.lattice = self.lattice.__class__(cell_matrix=self.scaling_matrix *
                                              self.lattice.matrix)

        tvecs = \
            np.asarray(
                np.asmatrix(supercell_lattice_points(self.scaling_matrix)) *
                self.lattice.matrix)

        basis = self.basis[:]
        self.basis = BasisAtoms()
        for atom in basis:
            for tvec in tvecs:
                xs, ys, zs = \
                    self.lattice.cartesian_to_fractional(atom.r + tvec)
                if self.wrap_coords:
                    xs, ys, zs = \
                        self.lattice.wrap_fractional_coordinate(
                            [xs, ys, zs])
                self.basis.append(
                    BasisAtom(atom.element,
                              lattice=self.lattice,
                              xs=xs,
                              ys=ys,
                              zs=zs))

    def rotate(self, **kwargs):
        """Rotate crystal cell lattice, basis, and unit cell."""
        if self.lattice is not None:
            self.lattice.rotate(**kwargs)
        if self.basis is not None:
            self.basis.rotate(**kwargs)
        self.unit_cell.rotate(**kwargs)

    def translate(self, t, fix_anchor_points=True):
        """Translate crystal cell basis."""
        if not fix_anchor_points and self.lattice is not None:
            self.lattice.translate(t)
        if self.basis is not None:
            self.basis.translate(t, fix_anchor_points=fix_anchor_points)
        self.unit_cell.translate(t, fix_anchor_points=fix_anchor_points)

    def update_basis(self, element, index=None, step=None):
        """Update a crystal cell basis element."""
        if index is None:
            [
                self.unit_cell.basis.__setitem__(i, element)
                for i in range(len(self.unit_cell.basis))
            ]
            [
                self.basis.__setitem__(i, element)
                for i in range(len(self.basis))
            ]
        elif isinstance(index, int):
            if step is None:
                step = self.unit_cell.basis.Natoms
            [
                self.unit_cell.basis.__setitem__(i, element)
                for i in range(index, len(self.unit_cell.basis), step)
            ]
            [
                self.basis.__setitem__(i, element)
                for i in range(index, len(self.basis), step)
            ]
        elif isinstance(index, (list, np.ndarray)):
            [self.unit_cell.basis.__setitem__(i, element) for i in index]
            [self.basis.__setitem__(i, element) for i in index]

    def todict(self):
        try:
            return dict(lattice=self.lattice,
                        basis=self.basis.symbols.tolist(),
                        coords=self.basis.rs.tolist(),
                        wrap_coords=self.wrap_coords,
                        unit_cell=self.unit_cell,
                        scaling_matrix=self.scaling_matrix.tolist())
        except AttributeError:
            return dict(lattice=self.lattice,
                        basis=None,
                        coords=None,
                        wrap_coords=self.wrap_coords,
                        unit_cell=self.unit_cell,
                        scaling_matrix=self.scaling_matrix.tolist())
Ejemplo n.º 14
0
class CrystalCell(BaseClass):
    """Class representation of crystal structure cell.

    Parameters
    ----------
    lattice : :class:`~sknano.core.crystallography.LatticeBase` sub-class
    basis : {:class:`~python:list`, :class:`~sknano.core.atoms.BasisAtoms`}
    coords : {:class:`~python:list`}, optional
    cartesian : {:class:`~python:bool`}, optional
    wrap_coords : {:class:`~python:bool`}, optional
    unit_cell : :class:`~sknano.core.crystallography.UnitCell`
    scaling_matrix : {:class:`~python:int`, :class:`~python:list`}

    """

    def __init__(self, lattice=None, basis=None, coords=None, cartesian=False,
                 wrap_coords=False, unit_cell=None, scaling_matrix=None):
        super().__init__()

        if unit_cell is None and basis is not None:
            basis = BasisAtoms(basis)
            basis.lattice = lattice
            if lattice is not None and coords is not None:
                for atom, pos in zip(basis, coords):
                    atom.lattice = lattice
                    if not cartesian:
                        atom.rs = pos
                    else:
                        atom.rs = lattice.cartesian_to_fractional(pos)

        # if basis is None:
        #     basis = BasisAtoms()

        # These attributes may be reset in the `@scaling_matrix.setter`
        # method and so they need to be initialized *before* setting
        # `self.scaling_matrix`.
        self.basis = basis
        self.lattice = lattice

        self.unit_cell = unit_cell
        self.wrap_coords = wrap_coords
        self.scaling_matrix = scaling_matrix

        self.fmtstr = \
            "lattice={lattice!r}, basis={basis!r}, coords={coords!r}, " + \
            "cartesian=False, wrap_coords={wrap_coords!r}, " + \
            "unit_cell={unit_cell!r}, scaling_matrix={scaling_matrix!r}"

    def __dir__(self):
        return ['lattice', 'basis', 'unit_cell', 'scaling_matrix']

    def __eq__(self, other):
        if all([attr is not None for attr in
                (self.scaling_matrix, self.unit_cell,
                 other.scaling_matrix, other.unit_cell)]) and \
                self.scaling_matrix.shape == other.scaling_matrix.shape:
            return self is other or \
                (self.unit_cell == other.unit_cell and
                 np.allclose(self.scaling_matrix, other.scaling_matrix))
        elif all([cell is not None for cell in
                  (self.unit_cell, other.unit_cell)]):
            return self is other or \
                (self.unit_cell == other.unit_cell and
                 all([mat is None for mat in
                      (self.scaling_matrix, other.scaling_matrix)]))

    def __lt__(self, other):
        return (self.unit_cell < other.unit_cell and
                self.scaling_matrix <= other.scaling_matrix) \
            or (self.unit_cell <= other.unit_cell and
                self.scaling_matrix < other.scaling_matrix)

    def __iter__(self):
        return iter(self.basis)

    def __getattr__(self, name):
        if name != 'lattice' and self.lattice is not None:
            try:
                return getattr(self.lattice, name)
            except AttributeError:
                pass
        if name != 'basis' and self.basis.Natoms != 0:
            try:
                return getattr(self.basis, name)
            except AttributeError:
                pass
        try:
            return getattr(self.unit_cell, name)
        except AttributeError:
            return super().__getattr__(name)

    @property
    def basis(self):
        return self._basis

    @basis.setter
    def basis(self, value):
        self._basis = value
        # if self.unit_cell is not None:
        #     self.unit_cell.basis[:] = \
        #         self.basis[:self.unit_cell.basis.Natoms]

    @property
    def lattice(self):
        return self._lattice

    @lattice.setter
    def lattice(self, value):
        self._lattice = value
        if self.basis is not None:
            self.basis.lattice = self.lattice

    @property
    def unit_cell(self):
        return self._unit_cell

    @unit_cell.setter
    def unit_cell(self, value):
        if value is not None and not isinstance(value, UnitCell):
            raise ValueError('Expected a `UnitCell` object')
        self._unit_cell = value
        if value is not None:
            if self.lattice is None:
                self._lattice = self.unit_cell.lattice
            if self.basis is None or self.basis.Natoms == 0:
                self._basis = self.unit_cell.basis

    @property
    def scaling_matrix(self):
        """Scaling matrix."""
        return self._scaling_matrix

    @scaling_matrix.setter
    def scaling_matrix(self, value):
        if value is None:
            self._scaling_matrix = np.asmatrix(np.ones(3, dtype=int))
            return

        if not isinstance(value, (int, float, tuple, list, np.ndarray)):
            return

        if isinstance(value, np.ndarray) and \
                ((value.shape == np.ones(3).shape and
                  np.allclose(value, np.ones(3))) or
                 (value.shape == np.eye(3).shape and
                  np.allclose(value, np.eye(3)))):
            self._scaling_matrix = np.asmatrix(value)
            return

        if isinstance(value, numbers.Number):
            value = self.lattice.nd * [int(value)]

        scaling_matrix = np.asmatrix(value, dtype=int)
        # scaling_matrix = np.asmatrix(value)
        if scaling_matrix.shape != self.lattice.matrix.shape:
            scaling_matrix = np.diagflat(scaling_matrix)
        self._scaling_matrix = scaling_matrix

        self.lattice = self.lattice.__class__(
            cell_matrix=self.scaling_matrix * self.lattice.matrix)

        tvecs = \
            np.asarray(
                np.asmatrix(supercell_lattice_points(self.scaling_matrix)) *
                self.lattice.matrix)

        basis = self.basis[:]
        self.basis = BasisAtoms()
        for atom in basis:
            for tvec in tvecs:
                xs, ys, zs = \
                    self.lattice.cartesian_to_fractional(atom.r + tvec)
                if self.wrap_coords:
                    xs, ys, zs = \
                        self.lattice.wrap_fractional_coordinate(
                            [xs, ys, zs])
                self.basis.append(BasisAtom(atom.element, lattice=self.lattice,
                                            xs=xs, ys=ys, zs=zs))

    def rotate(self, **kwargs):
        """Rotate crystal cell lattice, basis, and unit cell."""
        if self.lattice is not None:
            self.lattice.rotate(**kwargs)
        if self.basis is not None:
            self.basis.rotate(**kwargs)
        self.unit_cell.rotate(**kwargs)

    def translate(self, t, fix_anchor_points=True):
        """Translate crystal cell basis."""
        if not fix_anchor_points and self.lattice is not None:
            self.lattice.translate(t)
        if self.basis is not None:
            self.basis.translate(t, fix_anchor_points=fix_anchor_points)
        self.unit_cell.translate(t, fix_anchor_points=fix_anchor_points)

    def update_basis(self, element, index=None, step=None):
        """Update a crystal cell basis element."""
        if index is None:
            [self.unit_cell.basis.__setitem__(i, element)
             for i in range(len(self.unit_cell.basis))]
            [self.basis.__setitem__(i, element)
             for i in range(len(self.basis))]
        elif isinstance(index, int):
            if step is None:
                step = self.unit_cell.basis.Natoms
            [self.unit_cell.basis.__setitem__(i, element)
             for i in range(index, len(self.unit_cell.basis), step)]
            [self.basis.__setitem__(i, element)
             for i in range(index, len(self.basis), step)]
        elif isinstance(index, (list, np.ndarray)):
            [self.unit_cell.basis.__setitem__(i, element) for i in index]
            [self.basis.__setitem__(i, element) for i in index]

    def todict(self):
        try:
            return dict(lattice=self.lattice,
                        basis=self.basis.symbols.tolist(),
                        coords=self.basis.rs.tolist(),
                        wrap_coords=self.wrap_coords,
                        unit_cell=self.unit_cell,
                        scaling_matrix=self.scaling_matrix.tolist())
        except AttributeError:
            return dict(lattice=self.lattice, basis=None, coords=None,
                        wrap_coords=self.wrap_coords,
                        unit_cell=self.unit_cell,
                        scaling_matrix=self.scaling_matrix.tolist())