Source code for sknano.core.structures.unrolled_swnt

# -*- coding: utf-8 -*-
"""
==============================================================================
Unrolled SWNT structure class (:mod:`sknano.core.structures.unrolled_swnt`)
==============================================================================

.. currentmodule:: sknano.core.structures.unrolled_swnt

"""
from __future__ import absolute_import, division, print_function
from __future__ import unicode_literals
__docformat__ = 'restructuredtext en'

import numbers

import numpy as np

from sknano.core.atoms import BasisAtom as Atom, BasisAtoms as Atoms
from sknano.core.crystallography import Crystal3DLattice, UnitCell
# from sknano.core.math import Vector
# from sknano.core.refdata import aCC

from .base import NanoStructureBase
from .swnt import SWNTBase, compute_Ch, compute_T, compute_chiral_angle
from .graphene import GrapheneBase


__all__ = ['UnrolledSWNTMixin', 'UnrolledSWNTBase', 'UnrolledSWNT']


[docs]class UnrolledSWNTMixin: """Mixin class for unrolled nanotubes.""" @property def fix_Lx(self): """:class:`~python:bool` indicating whether \ :attr:`UnrolledSWNTMixin.Lx` is fixed or calculated.""" return self._fix_Lx @fix_Lx.setter def fix_Lx(self, value): if not isinstance(value, bool): raise TypeError('Expected `True` or `False`') self._fix_Lx = value self._integral_nx = False if self.fix_Lx else True @property def Lx(self): """Axis-aligned length along the `x`-axis in **Angstroms**.""" return self.nx * self.Ch @property def nx(self): """Number of unit cells along the :math:`x`-axis.""" return self._nx @nx.setter def nx(self, value): """Set :math:`n_x`""" if not (isinstance(value, numbers.Number) or value > 0): raise TypeError('Expected a real positive number.') self._nx = int(value) @property def Ly(self): """Axis-aligned length along the `y`-axis in **Angstroms**.""" return self.ny * self.layer_spacing @property def ny(self): """An alias for :attr:`UnrolledSWNTMixin.nlayers`.""" return self._nlayers @ny.setter def ny(self, value): self.nalyers = value @property def nlayers(self): """Number of layers.""" return self._nlayers @nlayers.setter def nlayers(self, value): """Set :attr:`nlayers`.""" if not (isinstance(value, numbers.Number) or value > 0): raise TypeError('Expected a real positive number.') self._nlayers = int(value)
[docs]class UnrolledSWNTBase(UnrolledSWNTMixin, SWNTBase, GrapheneBase, NanoStructureBase): """Base unrolled SWNT structure class.""" def __init__(self, *args, nx=1, Lx=None, fix_Lx=False, **kwargs): super().__init__(*args, **kwargs) self.fix_Lx = fix_Lx if Lx is not None: self.nx = float(Lx) / self.Ch elif nx is not None: self.nx = nx else: self.nx = 1 if self.nlayers > 1 and self.stacking_order == 'AB': chiral_angle = compute_chiral_angle(self.n, self.m, degrees=False) self.layer_shift.x = self.bond * np.cos(np.pi/6 - chiral_angle) self.layer_shift.z = -self.bond * np.sin(np.pi/6 - chiral_angle) self.generate_unit_cell() self.scaling_matrix = \ [int(np.ceil(self.nx)), self.ny, int(np.ceil(self.nz))] self.fmtstr = ", ".join((super().fmtstr, "nx={nx!r}", "Lx={Lx!r}"))
[docs] def todict(self): """Return :class:`~python:dict` of constructor parameters.""" attr_dict = super().todict() attr_dict.update(dict(nx=self.nx, Lx=self.Lx, fix_Lx=self.fix_Lx)) return attr_dict
[docs]class UnrolledSWNT(UnrolledSWNTBase, NanoStructureBase): """Unrolled SWNT structure class. Parameters ---------- *Ch : {:class:`python:tuple` or :class:`python:int`\ s} Either a 2-tuple of integers (i.e., *Ch = ((n, m)) or 2 integers (i.e., *Ch = (n, m) specifying the chiral indices of the nanotube chiral vector :math:`\\mathbf{C}_h = n\\mathbf{a}_1 + m\\mathbf{a}_2 = (n, m)`. nx : :class:`python:int`, optional Number of repeat unit cells in the :math:`x` direction, along the *unrolled* chiral vector. nz : :class:`python:int`, optional Number of repeat unit cells in the :math:`z` direction, along the *length* of the nanotube. basis : {:class:`python:list`}, optional List of :class:`python:str`\ s of element symbols or atomic number of the two atom basis (default: ['C', 'C']) .. versionadded:: 0.3.10 element1, element2 : {str, int}, optional Element symbol or atomic number of basis :class:`~sknano.core.Atom`\ s 1 and 2 .. deprecated:: 0.3.10 Use `basis` instead bond : float, optional :math:`\\mathrm{a}_{\\mathrm{CC}} =` distance between nearest neighbor atoms. Must be in units of **Angstroms**. nlayers : int, optional Number of layers (default: 1) layer_spacing : float, optional Distance between layers in **Angstroms** (default: 3.4). stacking_order : {'AA', 'AB'}, optional Stacking order of layers. layer_rotation_angles : list, optional list of rotation angles for each layer in **degrees** if `degrees` is `True` (default), otherwise in radians. The list length must equal the number of layers. layer_rotation_increment : float, optional incremental layer rotation angle in **degrees** if `degrees` is `True` (default), otherwise in radians. Each subsequent layer will be rotated by `layer_rotation_increment` relative to the layer below it. Lx : float, optional Length of the unrolled swnt sheet along the chiral vector in units of **Angstroms**. Overrides the `nx` value. .. versionchanged:: 0.4.0 Changed units from nanometers to **Angstroms** fix_Lx : bool, optional Generate the unrolled swnt sheet with the length along the chiral vector as close to the specified :math:`L_x` as possible. If `True`, then non integer :math:`n_x` cells are permitted. Lz : float, optional Length of the unrolled swnt sheet along the translation vector in units of **Angstroms**. Overrides the `nz` value. .. versionchanged:: 0.4.0 Changed units from nanometers to **Angstroms** fix_Lz : bool, optional Generate the unrolled swnt sheet with the length along the translation vector as close to the specified :math:`L_z` as possible. If `True`, then non integer :math:`n_z` cells are permitted. verbose : bool, optional if `True`, show verbose output Examples -------- >>> from sknano.core.structures import UnrolledSWNT >>> unrolled_swnt = UnrolledSWNT(10, 5) >>> unrolled_swnt UnrolledSWNT((10, 5), nx=1, nz=1, bond=1.42, basis=['C', 'C'], nlayers=1, layer_spacing=3.4, stacking_order='AB') """
[docs] 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)