Source code for sknano.core.structures.mwnt

# -*- coding: utf-8 -*-
"""
==============================================================================
MWNT structure class (:mod:`sknano.core.structures.mwnt`)
==============================================================================

.. currentmodule:: sknano.core.structures.mwnt

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

import numpy as np

from sknano.core.atoms import BasisAtoms
# from sknano.core.crystallography import UnitCell
from sknano.core.refdata import element_data

from .base import NanoStructureBase, r_CC_vdw
from .nanotube_bundle import NanotubeBundleBase
from .swnt import SWNT, NanotubeUnitCell, compute_dt  # , compute_T
from .extras import generate_Ch_list, get_chiral_type

__all__ = ['MWNTMixin', 'MWNTBase', 'MWNT']


nz_errmsg = ("Currently, setting `nz` requires specifying a single\n"
             "chiral type that is achiral ('armchair' or 'zigzag')\n"
             "or providing a list of chiralities that are\n"
             "of one achiral type.")


[docs]class MWNTMixin: """Mixin class for MWNTs.""" @property def Ch_list(self): """List of chiralities.""" return self._Ch_list @Ch_list.setter def Ch_list(self, value): if not isinstance(value, list): raise TypeError('Expected a list') self._Ch_list = value[:] @property def chiral_types(self): """List of chiral types for each `MWNT` wall.""" return [swnt.chiral_type for swnt in self.walls] @chiral_types.setter def chiral_types(self, value): if not isinstance(value, list): raise TypeError('Expected a list') self.update_Ch_list(chiral_types=value) @property def chiral_set(self): """Set of all chiral types in `MWNT`.""" return set(self.chiral_types) @property def dt(self): """`MWNT` wall diameters :math:`d_t=\\frac{|\\mathbf{C}_h|}{\\pi}` \ in \u212b.""" return self.walls[-1].dt @property def rt(self): """`MWNT` wall radii :math:`r_t=\\frac{|\\mathbf{C}_h|}{2\\pi}` \ in \u212b.""" return self.walls[-1].rt @property def Natoms(self): """Number of atoms in `MWNT`. **Returns total number of atoms in `MWNT`.** Use :attr:`~MWNT.Natoms_per_wall` to get a list of the number of atoms in each `MWNT` wall. .. math:: N_{\\mathrm{atoms}} = \\sum_{\\mathrm{walls}} """ return sum(self.Natoms_list) @property def Natoms_per_tube(self): """Number of atoms in `MWNT`.""" return self.Natoms @property def Nwalls(self): """Number of `MWNT` walls.""" return len(self.Ch_list) @Nwalls.setter def Nwalls(self, value): self.update_Ch_list(Nwalls=value) @property def min_wall_diameter(self): """Minimum wall diameter.""" return self._min_wall_diameter @min_wall_diameter.setter def min_wall_diameter(self, value): self._min_wall_diameter = value self.update_Ch_list() @property def max_wall_diameter(self): """Maximum wall diameter.""" return self._max_wall_diameter @max_wall_diameter.setter def max_wall_diameter(self, value): self._max_wall_diameter = value self.update_Ch_list() @property def max_walls(self): """Maximum number of walls.""" return self._max_walls @max_walls.setter def max_walls(self, value): self._max_walls = value @property def wall_spacing(self): """Wall-to-wall spacing.""" return self._wall_spacing @wall_spacing.setter def wall_spacing(self, value): self._wall_spacing = value self.update_Ch_list() @property def mass(self): """MWNT mass in **grams**.""" return sum(self.wall_masses) @property def tube_mass(self): """An alias for :attr:`~MWNTMixin.mass`.""" return self.mass @property def Lz(self): """MWNT length in Angstroms.""" Lz = self._Lz if Lz is None: return self._nz * self.T_list[0] else: return Lz @Lz.setter def Lz(self, value): self._Lz = value @property def nz(self): """Number of nanotube unit cells along the :math:`z`-axis.""" try: return self._nz except AttributeError: return None @property def Natoms_per_wall(self): """Alias for :attr:`MWNT.Natoms_list`""" return self.Natoms_list @property def Natoms_list(self): """List of `MWNT` `SWNT` wall's number of atoms \ :attr:`~SWNT.Natoms`.""" return [swnt.Natoms for swnt in self.walls] @property def nz_list(self): """Number of nanotube unit cells along the :math:`z`-axis.""" return [swnt.nz for swnt in self.walls] @property def Lz_list(self): """MWNT length :math:`L_z = L_{\\mathrm{tube}}` in **Angstroms**.""" return [swnt.Lz for swnt in self.walls] @property def T_list(self): """Length of `MWNT` unit cell :math:`|\\mathbf{T}|` in \u212b.""" return [swnt.T for swnt in self.walls] @property def dt_list(self): """List of `MWNT` `SWNT` wall diameters :attr:`~SWNT.dt` \ :math:`d_t=\\frac{|\\mathbf{C}_h|}{\\pi}` in \u212b.""" return [swnt.dt for swnt in self.walls] @property def rt_list(self): """List of `MWNT` `SWNT` wall radii :attr:`~SWNT.rt` \ :math:`r_t=\\frac{|\\mathbf{C}_h|}{2\\pi}` in \u212b.""" return [swnt.rt for swnt in self.walls] @property def wall_masses(self): """List of `MWNT` `SWNT` wall diameters :attr:`~SWNT.dt` \ :math:`d_t=\\frac{|\\mathbf{C}_h|}{\\pi}` in \u212b.""" return [swnt.tube_mass for swnt in self.walls] @property def wall_diameters(self): """Alias for :attr:`MWNTMixin.dt_list`.""" return self.dt_list @property def wall_radii(self): """Alias for :attr:`MWNTMixin.rt_list`.""" return self.rt_list @property def walls(self): """List of `MWNT` `SWNT` wall structures.""" return [SWNT(Ch, nz=self._nz, Lz=self._Lz, fix_Lz=self.fix_Lz, basis=self.basis, bond=self.bond) for Ch in self.Ch_list]
[docs] def get_wall(self, Ch): """Return the :class:`~sknano.core.structures.SWNT` structure with \ chirality `Ch`. """ return SWNT(Ch, nz=self._nz, Lz=self._Lz, fix_Lz=self.fix_Lz, basis=self.basis, bond=self.bond) if Ch in self.Ch_list \ else None
[docs] def generate_dt_mask(self, dt, max_dt_diff=0.5): """Generate boolean mask array. Parameters ---------- dt : float max_dt_diff : float, optional Returns ------- dt_mask : :class:`~numpy:numpy.ndarray` """ dt_mask = np.abs(self._dt_pool - dt) <= max_dt_diff while not np.any(dt_mask): max_dt_diff += max_dt_diff dt_mask = np.abs(self._dt_pool - dt) <= max_dt_diff return dt_mask
[docs] def generate_Ch_list(self, Nwalls=None, max_walls=None, min_wall_diameter=None, max_wall_diameter=None, chiral_types=None, wall_spacing=None): """Generate list of chiralities.""" if Nwalls is not None: max_walls = Nwalls if max_walls is None: max_walls = 5 if max_wall_diameter is None: max_wall_diameter = np.inf if min_wall_diameter is None: min_wall_diameter = 5.0 if wall_spacing is None: wall_spacing = 2 * element_data['C']['VanDerWaalsRadius'] delta_dt = 2 * wall_spacing imax = 100 self._Ch_pool = \ np.asarray(generate_Ch_list(imax=imax, chiral_types=chiral_types)) self._dt_pool = np.asarray([compute_dt(_Ch, bond=self.bond) for _Ch in self._Ch_pool]) dt_mask = np.logical_and(self._dt_pool >= min_wall_diameter, self._dt_pool <= max_wall_diameter) self._Ch_pool = self._Ch_pool[dt_mask] self._dt_pool = self._dt_pool[dt_mask] if max_wall_diameter < np.inf: dt_list = [] dt = self._dt_pool.min() while dt <= max_wall_diameter and len(dt_list) < max_walls: dt_list.append(dt) dt += delta_dt else: dt_list = [self._dt_pool.min() + i * delta_dt for i in range(max_walls)] dt_masks = [self.generate_dt_mask(_dt) for _dt in dt_list] return [tuple(self._Ch_pool[_mask][np.random.choice( list(range(len(self._Ch_pool[_mask]))))].tolist()) for _mask in dt_masks]
[docs] def update_Ch_list(self, Nwalls=None, min_wall_diameter=None, max_wall_diameter=None, wall_spacing=None, chiral_types=None): """Update :attr:`MWNTMixin.Ch_list`.""" if Nwalls is None: Nwalls = self.Nwalls if min_wall_diameter is None: min_wall_diameter = self.min_wall_diameter if max_wall_diameter is None: max_wall_diameter = self.max_wall_diameter if wall_spacing is None: wall_spacing = self.wall_spacing self.Ch_list = \ self.generate_Ch_list(Nwalls=Nwalls, min_wall_diameter=min_wall_diameter, max_wall_diameter=max_wall_diameter, chiral_types=chiral_types, wall_spacing=wall_spacing)
[docs]class MWNTBase(MWNTMixin, NanoStructureBase): """MWNT structure class. Parameters ---------- Ch_list : :class:`python:list`, optional (:attr:`~SWNT.n`, :attr:`~SWNT.m`) for each `SWNT` wall in `MWNT`. Nwalls : int, optional Number of `SWNT` walls in `MWNT`. Lz : float, optional `MWNT` length in **Angstroms**. min_wall_diameter : float, optional Minimum `MWNT` wall diameter, in units of **Angstroms**. max_wall_diameter : float, optional Maximum `MWNT` wall diameter, in units of **Angstroms**. max_walls : int, optional Maximum number of `MWNT` walls. chiral_types : {None, 'armchair', 'zigzag', 'achiral', 'chiral'}, optional If `None`, the :attr:`~SWNT.chiral_type` of each `MWNT` walls will be random and determined by the set of randomly selected chiral indices (:attr:`~SWNT.n`, :attr:`~SWNT.m`). wall_spacing : float, optional Inter-wall spacing in units of **Angstroms**. Default value is the van der Waals interaction distance of 3.35 Angstroms. 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` 1 and 2 .. deprecated:: 0.3.10 Use `basis` instead bond : float, optional :math:`\\mathrm{a}_{\\mathrm{CC}} =` distance between nearest neighbor atoms, in units of **Angstroms**. verbose : bool, optional if `True`, show verbose output """ def __init__(self, Ch_list=None, Nwalls=None, Lz=None, nz=None, min_wall_diameter=None, max_wall_diameter=None, max_walls=None, chiral_types=None, wall_spacing=2 * r_CC_vdw, **kwargs): Ch_list = kwargs.pop('Ch', Ch_list) super().__init__(**kwargs) self._nz = nz if nz is not None: if Ch_list is not None: chiral_set = set(get_chiral_type(Ch) for Ch in Ch_list) if chiral_types is not None: chiral_set = set(chiral_types) if Ch_list is None and chiral_set is None or \ (len(chiral_set) > 1 or 'chiral' in chiral_set): raise ValueError(nz_errmsg) self.fix_Lz = False else: self.fix_Lz = True if Ch_list is None or not isinstance(Ch_list, list): Ch_list = \ self.generate_Ch_list(Nwalls=Nwalls, max_walls=max_walls, min_wall_diameter=min_wall_diameter, max_wall_diameter=max_wall_diameter, chiral_types=chiral_types, wall_spacing=wall_spacing) else: Ch_list = [tuple(Ch) for Ch in Ch_list] self.Ch_list = Ch_list[:] self._min_wall_diameter = min_wall_diameter self._max_wall_diameter = max_wall_diameter self._max_walls = max_walls self._wall_spacing = wall_spacing if nz is None and Lz is None: Lz = 10.0 self._Lz = Lz self.generate_unit_cell() if self.verbose: print(self.walls) fmtstr = ", ".join((super().fmtstr, "Ch_list={Ch_list!r}")) if self.fix_Lz: fmtstr = ", ".join((fmtstr, "Lz={Lz!r}")) else: fmtstr = ", ".join((fmtstr, "nz={nz!r}")) self.fmtstr = ', '.join((fmtstr, "min_wall_diameter={min_wall_diameter!r}", "max_wall_diameter={max_wall_diameter!r}", "max_walls={max_walls!r}", "chiral_types={chiral_types!r}", "wall_spacing={wall_spacing!r}"))
[docs] def generate_unit_cell(self): """Generate Nanotube unit cell.""" outside_unit_cell = self.get_wall(self.Ch_list[-1]).unit_cell basis = BasisAtoms() [basis.extend(swnt.unit_cell.basis) for swnt in self.walls] self.unit_cell = NanotubeUnitCell(lattice=outside_unit_cell.lattice, basis=basis)
[docs] def todict(self): """Return :class:`~python:dict` of `MWNT` attributes.""" attr_dict = super().todict() attr_dict.update(dict(Ch_list=self.Ch_list, nz=self._nz, Lz=self._Lz, fix_Lz=self.fix_Lz, min_wall_diameter=self.min_wall_diameter, max_wall_diameter=self.max_wall_diameter, max_walls=self.max_walls, chiral_types=self.chiral_types, wall_spacing=self.wall_spacing)) return attr_dict
[docs]class MWNT(NanotubeBundleBase, MWNTBase): """MWNT structure class. Parameters ---------- Ch_list : :class:`python:list`, optional (:attr:`~SWNT.n`, :attr:`~SWNT.m`) for each `SWNT` wall in `MWNT`. Nwalls : int, optional Number of `SWNT` walls in `MWNT`. Lz : float, optional `MWNT` length in **Angstroms**. .. versionchanged:: 0.4.0 Changed units from nanometers to **Angstroms** min_wall_diameter : float, optional Minimum `MWNT` wall diameter, in units of **Angstroms**. max_wall_diameter : float, optional Maximum `MWNT` wall diameter, in units of **Angstroms**. max_walls : int, optional Maximum number of `MWNT` walls. chiral_types : {None, 'armchair', 'zigzag', 'achiral', 'chiral'}, optional If `None`, the :attr:`~SWNT.chiral_type` of each `MWNT` walls will be random and determined by the set of randomly selected chiral indices (:attr:`~SWNT.n`, :attr:`~SWNT.m`). wall_spacing : float, optional Inter-wall spacing in units of **Angstroms**. Default value is the van der Waals interaction distance of 3.35 Angstroms. 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` 1 and 2 .. deprecated:: 0.3.10 Use `basis` instead bond : float, optional :math:`\\mathrm{a}_{\\mathrm{CC}} =` distance between nearest neighbor atoms, in units of **Angstroms**. nx, ny : int, optional Number of repeat unit cells in the :math:`x, y` dimensions. vdw_radius : float, optional van der Waals radius of nanotube atoms bundle_packing : {'hcp', 'ccp'}, optional Packing arrangement of MWNT bundles. If `bundle_packing` is `None`, then it will be determined by the `bundle_geometry` parameter if `bundle_geometry` is not `None`. If both `bundle_packing` and `bundle_geometry` are `None`, then `bundle_packing` defaults to `hcp`. bundle_geometry : {'triangle', 'hexagon', 'square', 'rectangle'}, optional verbose : bool, optional if `True`, show verbose output Examples -------- >>> from sknano.generators import MWNT """ pass