r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1"""Implements the Topology-Scaling Algorithm (TSA)

3Method is described in:

4Topology-Scaling Identification of Layered Solids and Stable Exfoliated

52D Materials

6M. Ashton, J. Paul, S.B. Sinnott, and R.G. Hennig

7Phys. Rev. Lett. 118, 106101

82017

11A disjoint set is used here to allow insertion of bonds one at a time.

12This permits k-interval analysis.

13"""

16import itertools

17import numpy as np

18from ase.geometry.dimensionality.disjoint_set import DisjointSet

21class TSA:

23 def __init__(self, num_atoms, n=2):

24 """Initializes the TSA class.

26 A disjoint set is maintained for the single cell and for the supercell.

27 For some materials, such as interpenetrating networks,

28 the dimensionality classification is dependent on the size of the

29 initial cell.

31 Parameters:

33 num_atoms: int The number of atoms in the unit cell.

34 n: int The number size of the (n, n, n) periodic supercell.

35 """

36 self.n = n

37 self.num_atoms = num_atoms

38 self.gsingle = DisjointSet(num_atoms)

39 self.gsuper = DisjointSet(num_atoms * n**3)

41 self.m = [1, n, n**2]

42 self.cells = np.array(list(itertools.product(range(n), repeat=3)))

43 self.offsets = num_atoms * np.dot(self.m, self.cells.T)

45 def insert_bond(self, i, j, offset):

46 """Inserts a bond into the component graph, both in the single cell and

47 each of the n^3 subcells of the supercell.

49 Parameters:

51 i: int The index of the first atom.

52 n: int The index of the second atom.

53 offset: tuple The cell offset of the second atom.

54 """

55 nbr_cells = (self.cells + offset) % self.n

56 nbr_offsets = self.num_atoms * np.dot(self.m, nbr_cells.T)

58 self.gsingle.union(i, j)

59 for (a, b) in zip(self.offsets, nbr_offsets):

60 self.gsuper.union(a + i, b + j)

61 self.gsuper.union(b + i, a + j)

63 def _get_component_dimensionalities(self):

65 n = self.n

66 offsets = self.offsets

67 single_roots = np.unique(self.gsingle.find_all())

68 super_components = self.gsuper.find_all()

70 component_dim = {}

71 for i in single_roots:

73 num_clusters = len(np.unique(super_components[offsets + i]))

74 dim = {n**3: 0, n**2: 1, n: 2, 1: 3}[num_clusters]

75 component_dim[i] = dim

76 return component_dim

78 def check(self):

79 """Determines the dimensionality histogram.

81 Returns:

82 hist : tuple Dimensionality histogram.

83 """

84 cdim = self._get_component_dimensionalities()

85 hist = np.zeros(4).astype(int)

86 bc = np.bincount(list(cdim.values()))

87 hist[:len(bc)] = bc

88 return tuple(hist)

90 def get_components(self):

91 """Determines the dimensionality and constituent atoms of each

92 component.

94 Returns:

95 components: array The component ID every atom

96 """

97 relabelled_dim = {}

98 relabelled_components = self.gsingle.find_all(relabel=True)

99 cdim = self._get_component_dimensionalities()

100 for k, v in cdim.items():

101 relabelled_dim[relabelled_components[k]] = v

103 return relabelled_components, relabelled_dim