Source code for sknano.structures._mwnt
# -*- coding: utf-8 -*-
"""
==============================================================================
MWNT structure class (:mod:`sknano.structures._mwnt`)
==============================================================================
.. currentmodule:: sknano.structures._mwnt
"""
from __future__ import absolute_import, division, print_function, \
unicode_literals
from six.moves import range
__docformat__ = 'restructuredtext en'
import numbers
import numpy as np
from sknano.core.refdata import dVDW
from ._base import StructureBase
from ._swnt import SWNT
from ._compute_funcs import compute_dt
__all__ = ['MWNT']
[docs]class MWNT(StructureBase):
"""MWNT structure class."""
def __init__(self, Ch=None, Nwalls=None, Lz=None, fix_Lz=False, nz=1,
add_inner_shells=False, add_outer_shells=True,
max_shells=None, max_shell_diameter=np.inf,
min_shells=None, min_shell_diameter=0.0,
new_shell_type=None, shell_spacing=dVDW, **kwargs):
super(MWNT, self).__init__(**kwargs)
self.Ch = Ch
self.shells = []
if Ch is None or not isinstance(Ch, list):
self.Ch = []
self._add_inner_shells = add_inner_shells
self._add_outer_shells = add_outer_shells
self._starting_shell_position = 'outer'
if max_shells is None:
max_shells = 10
self.max_shells = max_shells
self.max_shell_diameter = max_shell_diameter
if min_shells is None:
min_shells = 2
self.min_shells = min_shells
self.min_shell_diameter = min_shell_diameter
self.new_shell_type = new_shell_type
self.shell_spacing = shell_spacing
Ch = []
dt = []
for n in range(0, 501):
for m in range(0, 501):
if (n <= 2 and m <= 2):
continue
else:
Ch.append((n, m))
dt.append(compute_dt(n, m, bond=self.bond))
Ch = np.asarray(Ch)
dt = np.asarray(dt)
self.max_shell_diameter = min(self.max_shell_diameter, dt.max())
self.min_shell_diameter = max(self.min_shell_diameter, dt.min())
delta_dt = -2 * self.shell_spacing
max_dt_diff = 0.05
if self._add_outer_shells:
delta_dt = -delta_dt
self._starting_shell_position = 'inner'
next_dt = dt[np.where(dt >= self.min_shell_diameter)][0] + delta_dt
while len(self.Ch) < self.max_shells and \
next_dt <= self.max_shell_diameter and \
next_dt >= self.min_shell_diameter:
# get chiral indices for next_dt
next_Ch_candidates = []
while len(next_Ch_candidates) == 0 and \
next_dt <= self.max_shell_diameter and \
next_dt >= self.min_shell_diameter:
if self.new_shell_type in ('AC', 'armchair'):
next_Ch_candidates = \
Ch[np.where(
np.logical_and(
np.abs(dt - next_dt) <= max_dt_diff,
Ch[:,0] == Ch[:,1]))]
elif self.new_shell_type in ('ZZ', 'zigzag'):
next_Ch_candidates = \
Ch[np.where(
np.logical_and(
np.abs(dt - next_dt) <= max_dt_diff,
np.logical_or(Ch[:,0] == 0,
Ch[:,1] == 0)))]
elif self.new_shell_type == 'achiral':
next_Ch_candidates = \
Ch[np.where(
np.logical_and(
np.abs(dt - next_dt) <= max_dt_diff,
np.logical_or(Ch[:,0] == Ch[:,1],
np.logical_or(
Ch[:,0] == 0,
Ch[:,1] == 0))))]
elif self.new_shell_type == 'chiral':
next_Ch_candidates = \
Ch[np.where(
np.logical_and(
np.abs(dt - next_dt) <= max_dt_diff,
np.logical_and(Ch[:,0] != Ch[:,1],
np.logical_and(
Ch[:,0] != 0,
Ch[:,1] != 1))))]
else:
next_Ch_candidates = \
Ch[np.where(np.abs(dt - next_dt) <= max_dt_diff)]
if self._add_outer_shells:
next_dt += max_dt_diff
else:
next_dt -= max_dt_diff
if len(next_Ch_candidates) > 0:
n, m = next_Ch_candidates[
np.random.choice(np.arange(len(next_Ch_candidates)))]
self.Ch.append((n, m))
next_dt += delta_dt
else:
break
for Ch in self.Ch:
self.shells.append(SWNT(Ch[0], Ch[-1], Lz=Lz, fix_Lz=fix_Lz,
nz=nz, **kwargs))
self.fix_Lz = fix_Lz
if nz is not None:
self.nz = nz
else:
self.nz = 1
if self.verbose:
print(self.shells)
def __str__(self):
return repr(self)
def __repr__(self):
"""Return canonical string representation of `MWNT`."""
strrep = "MWNT(Ch={!r}, Nwalls={!r}, Lz={!r}, fix_Lz={!r}, " + \
"nz={!r}, element1={!r}, element2={!r}, bond={!r})"
return strrep.format(self.Ch, self.Nwalls, self.Lz, self.fix_Lz,
self.nz, self.element1, self.element2, self.bond)
@property
def chiral_types(self):
return [swnt.chiral_type for swnt in self.shells]
@property
def chiral_set(self):
return set(self.chiral_types)
@property
def nz(self):
"""Number of nanotube unit cells along the :math:`z`-axis."""
if len(self.chiral_set) == 1:
return self._nz
return [swnt.nz for swnt in self.shells]
@nz.setter
def nz(self, value):
"""Set number of nanotube unit cells along the :math:`z`-axis."""
if not (isinstance(value, numbers.Real) or value > 0):
raise TypeError('Expected a real, positive number.')
self._nz = value
if self._assert_integer_nz:
self._nz = int(np.ceil(value))
@property
def Lz(self):
"""MWNT length :math:`L_z = L_{\\mathrm{tube}}` in **nanometers**."""
if len(self.chiral_set) == 1:
return self.nz * self.T
return np.asarray([swnt.Lz for swnt in self.shells]).max()
@property
def fix_Lz(self):
return self._fix_Lz
@fix_Lz.setter
def fix_Lz(self, value):
if not isinstance(value, bool):
raise TypeError('Expected `True` or `False`')
self._fix_Lz = value
self._assert_integer_nz = True
if self.fix_Lz:
self._assert_integer_nz = False
@property
def Nwalls(self):
return len(self.shells)
@property
def T(self):
"""Length of `MWNT` unit cell :math:`|\\mathbf{T}|` in \u212b."""
if len(self.chiral_set) == 1:
return self.shells[-1].T
return np.asarray([swnt.T for swnt in self.shells]).max()
@property
def dt(self):
"""`MWNT` diameter :math:`d_t=\\frac{|\\mathbf{C}_h|}{\\pi}` \
in \u212b."""
return np.asarray([swnt.dt for swnt in self.shells]).max()
@property
def rt(self):
"""`MWNT` radius :math:`r_t=\\frac{|\\mathbf{C}_h|}{2\\pi}` \
in \u212b."""
return np.asarray([swnt.rt for swnt in self.shells]).max()
@property
def Natoms(self):
"""Number of atoms in nanotube.
.. versionchanged:: 0.3.0
**Returns total number of atoms per nanotube.**
Use :attr:`~MWNT.Natoms_per_unit_cell` to get the number of
atoms per unit cell.
.. math::
N_{\\mathrm{atoms}} = 2N\\times n_z =
\\frac{4(n^2 + m^2 + nm)}{d_R}\\times n_z
where :math:`N` is the number of graphene hexagons mapped to the
nanotube unit cell and :math:`n_z` is the number of unit cells.
"""
return np.asarray([swnt.Natoms for swnt in self.shells]).sum()
@property
def Natoms_per_tube(self):
"""Number of atoms in nanotube :math:`N_{\\mathrm{atoms/tube}}`."""
return self.Natoms
@property
def Natoms_per_unit_cell(self):
"""Number of atoms in nanotube unit cell.
.. math::
N_{\\mathrm{atoms}} = 2N = \\frac{4(n^2 + m^2 + nm)}{d_R}
where :math:`N` is the number of graphene hexagons mapped to the
nanotube unit cell.
"""
return np.asarray([swnt.Natoms_per_unit_cell for swnt in
self.shells]).sum()
@property
def unit_cell_mass(self):
"""Unit cell mass in atomic mass units."""
return np.asarray([swnt.unit_cell_mass for swnt in self.shells]).sum()
@property
def Ntubes(self):
"""Number of `MWNT`."""
return 1
@property
def Nshells(self):
return self.Nwalls
@property
def tube_mass(self):
"""MWNT mass in **grams**."""
return np.asarray([swnt.tube_mass for swnt in self.shells]).sum()
[docs] def todict(self):
return dict(Ch=self.Ch, Nwalls=self.Nwalls, nz=self.nz, Lz=self.Lz,
fix_Lz=self.fix_Lz, element1=self.element1,
element2=self.element2, bond=self.bond)