Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1from copy import deepcopy 

2from icet.core.structure import Structure 

3from typing import List, Iterator 

4from ase import Atoms 

5import copy 

6from itertools import product 

7from string import ascii_uppercase 

8import numpy as np 

9from icet.tools.geometry import chemical_symbols_to_numbers 

10 

11 

12class Sublattice: 

13 """ 

14 This class stores and provides information about a specific 

15 sublattice. A sublattice is always supercell specific since 

16 it contains lattice indices. 

17 

18 Parameters 

19 ---------- 

20 chemical_symbols 

21 the allowed species on this sublattice 

22 indices 

23 the lattice indices the sublattice consists of 

24 symbol 

25 string used to mark the sublattice 

26 """ 

27 

28 def __init__(self, 

29 chemical_symbols: List[str], 

30 indices: List[int], 

31 symbol: str): 

32 self._chemical_symbols = chemical_symbols 

33 self._indices = indices 

34 self._symbol = symbol 

35 self._numbers = chemical_symbols_to_numbers(chemical_symbols) 

36 

37 @property 

38 def chemical_symbols(self): 

39 return copy.deepcopy(self._chemical_symbols) 

40 

41 @property 

42 def atomic_numbers(self): 

43 return self._numbers.copy() 

44 

45 @property 

46 def indices(self): 

47 return self._indices.copy() 

48 

49 @property 

50 def symbol(self): 

51 """Symbol representation of sublattice, i.e. A, B, C, etc..""" 

52 return self._symbol 

53 

54 

55class Sublattices: 

56 """ 

57 This class stores and provides information about the sublattices 

58 of a structure. 

59 

60 Parameters 

61 ---------- 

62 allowed_species 

63 list of the allowed species on each site of the primitve 

64 structure. For example this can be the chemical_symbols from 

65 a cluster space 

66 primitive_structure 

67 the primitive structure the allowed species reference to 

68 structure 

69 the structure that the sublattices will be based on 

70 fractional_position_tolerance 

71 tolerance applied when comparing positions in fractional coordinates 

72 """ 

73 

74 def __init__(self, 

75 allowed_species: List[List[str]], 

76 primitive_structure: Atoms, 

77 structure: Atoms, 

78 fractional_position_tolerance: float): 

79 self._structure = structure 

80 # sorted unique sites, this basically decides A, B, C... sublattices 

81 active_lattices = sorted(set([tuple(sorted(symbols)) 

82 for symbols in allowed_species if len(symbols) > 1])) 

83 inactive_lattices = sorted( 

84 set([tuple(sorted(symbols)) for symbols in allowed_species if len(symbols) == 1])) 

85 self._allowed_species = active_lattices + inactive_lattices 

86 

87 n = int(np.sqrt(len(self._allowed_species))) + 1 

88 symbols = [''.join(p) for r in range(1, n+1) for p in product(ascii_uppercase, repeat=r)] 

89 

90 cpp_prim_structure = Structure.from_atoms(primitive_structure) 

91 self._sublattices = [] 

92 sublattice_to_indices = [[] for _ in range(len(self._allowed_species))] 

93 for index, position in enumerate(structure.positions): 

94 lattice_site = cpp_prim_structure.find_lattice_site_by_position( 

95 position=position, fractional_position_tolerance=fractional_position_tolerance) 

96 

97 # Get allowed species on this site 

98 species = allowed_species[lattice_site.index] 

99 

100 # Get what sublattice those species correspond to 

101 sublattice = self._allowed_species.index(tuple(sorted(species))) 

102 

103 sublattice_to_indices[sublattice].append(index) 

104 

105 for symbol, species, indices in zip(symbols, self._allowed_species, sublattice_to_indices): 

106 sublattice = Sublattice(chemical_symbols=species, indices=indices, symbol=symbol) 

107 self._sublattices.append(sublattice) 

108 

109 # Map lattice index to sublattice index 

110 self._index_to_sublattice = {} 

111 for k, sublattice in enumerate(self): 

112 for index in sublattice.indices: 

113 self._index_to_sublattice[index] = k 

114 

115 def __getitem__(self, key: int) -> Sublattice: 

116 """ Returns a sublattice according to key. """ 

117 return self._sublattices[key] 

118 

119 def __len__(self): 

120 """ Returns number of sublattices. """ 

121 return len(self._sublattices) 

122 

123 def __iter__(self) -> Iterator[Sublattice]: 

124 """ Generator over sublattices. """ 

125 yield from self._sublattices 

126 

127 def get_sublattice_index(self, index: int) -> int: 

128 """ Returns the index of the sublattice the symbol 

129 or index in the structure belongs to. 

130 

131 Parameters 

132 ---------- 

133 index 

134 index of site in the structure 

135 """ 

136 return self._index_to_sublattice[index] 

137 

138 @property 

139 def allowed_species(self) -> List[List[str]]: 

140 """Lists of the allowed species on each sublattice, in order. 

141 """ 

142 return deepcopy(self._allowed_species) 

143 

144 def get_sublattice_sites(self, index: int) -> List[int]: 

145 """Returns the sites that belong to the sublattice with the 

146 corresponding index. 

147 

148 Parameters 

149 ---------- 

150 index 

151 index of the sublattice 

152 """ 

153 return self[index].indices 

154 

155 def get_allowed_symbols_on_site(self, index: int) -> List[str]: 

156 """Returns the allowed symbols on the site. 

157 

158 Parameters 

159 ----------- 

160 index 

161 lattice site index 

162 """ 

163 return self[self._index_to_sublattice[index]].chemical_symbols 

164 

165 def get_allowed_numbers_on_site(self, index: int) -> List[int]: 

166 """Returns the allowed atomic numbers on the site. 

167 

168 Parameters 

169 ----------- 

170 index 

171 lattice site index 

172 """ 

173 return self[self._index_to_sublattice[index]].atomic_numbers 

174 

175 @property 

176 def active_sublattices(self) -> List[Sublattice]: 

177 """ List of the active sublattices. """ 

178 return [sl for sl in self if len(sl.chemical_symbols) > 1] 

179 

180 @property 

181 def inactive_sublattices(self) -> List[Sublattice]: 

182 """ List of the active sublattices. """ 

183 return [sl for sl in self if len(sl.chemical_symbols) == 1] 

184 

185 def assert_occupation_is_allowed(self, chemical_symbols: List[str]): 

186 """Asserts that the current occupation obeys the sublattices.""" 

187 if len(chemical_symbols) != len(self._structure): 

188 raise ValueError("len of input chemical symbols ({}) do not match len of supercell" 

189 " ({})".format(len(chemical_symbols), len(self._structure))) 

190 for sl in self: 

191 for i in sl.indices: 

192 if not chemical_symbols[i] in sl.chemical_symbols: 

193 msg = ('Occupations of structure not compatible with the sublattice.' 

194 ' Site {} with occupation {} not allowed on sublattice {}' 

195 .format(i, chemical_symbols[i], sl.chemical_symbols)) 

196 raise ValueError(msg)