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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

from copy import deepcopy 

from icet.core.structure import Structure 

from typing import List 

from ase import Atoms 

import copy 

from itertools import product 

from string import ascii_uppercase 

import numpy as np 

from icet.tools.geometry import chemical_symbols_to_numbers 

 

 

class Sublattice: 

""" 

This class stores and provides information about a specific 

sublattice. A sublattice is always supercell specific since 

it contains lattice indices. 

 

Parameters 

---------- 

chemical_symbols 

the allowed species on this sublattice 

indices 

the lattice indices the sublattice consists of 

 

""" 

 

def __init__(self, chemical_symbols: List[str], indices: List[int], symbol: str): 

self._chemical_symbols = chemical_symbols 

self._indices = indices 

self._symbol = symbol 

self._numbers = chemical_symbols_to_numbers(chemical_symbols) 

 

@property 

def chemical_symbols(self): 

return copy.deepcopy(self._chemical_symbols) 

 

@property 

def atomic_numbers(self): 

return self._numbers.copy() 

 

@property 

def indices(self): 

return self._indices.copy() 

 

@property 

def symbol(self): 

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

return self._symbol 

 

 

class Sublattices: 

""" 

This class stores and provides information about the sublattices 

of a structure. 

 

Parameters 

---------- 

allowed_species 

list of the allowed species on each site of the primitve 

structure. For example this can be the chemical_symbols from 

a cluster space 

primitive_structure 

the primitive structure the allowed species reference to 

structure 

the structure that the sublattices will be based on 

""" 

 

def __init__(self, allowed_species: List[List[str]], primitive_structure: Atoms, 

structure: Atoms): 

self._structure = structure 

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

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

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

inactive_lattices = sorted( 

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

self._allowed_species = active_lattices + inactive_lattices 

 

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

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

 

cpp_prim_structure = Structure.from_atoms(primitive_structure) 

self._sublattices = [] 

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

for index, position in enumerate(structure.get_positions()): 

lattice_site = cpp_prim_structure.find_lattice_site_by_position(position) 

 

# Get allowed species on this site 

species = allowed_species[lattice_site.index] 

 

# Get what sublattice those species correspond to 

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

 

sublattice_to_indices[sublattice].append(index) 

 

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

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

self._sublattices.append(sublattice) 

 

# Map lattice index to sublattice index 

self._index_to_sublattice = {} 

for k, sublattice in enumerate(self): 

for index in sublattice.indices: 

self._index_to_sublattice[index] = k 

 

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

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

return self._sublattices[key] 

 

def __len__(self): 

""" Returns number of sublattices. """ 

return len(self._sublattices) 

 

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

""" Returns the index of the sublattice the symbol 

or index in the structure belongs to. 

 

Parameters 

---------- 

index 

index of site in the structure 

""" 

return self._index_to_sublattice[index] 

 

@property 

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

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

""" 

return deepcopy(self._allowed_species) 

 

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

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

corresponding index. 

 

Parameters 

---------- 

index 

index of the sublattice 

""" 

return self[index].indices 

 

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

"""Returns the allowed symbols on the site. 

 

Parameters 

----------- 

index 

lattice site index 

""" 

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

 

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

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

 

Parameters 

----------- 

index 

lattice site index 

""" 

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

 

@property 

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

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

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

 

@property 

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

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

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

 

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

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

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

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

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

for sl in self: 

for i in sl.indices: 

if not chemical_symbols[i] in sl.chemical_symbols: 

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

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

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

raise ValueError(msg)