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

""" 

This module provides a Python interface to the PermutationMatrix 

class with supplementary functions. 

""" 

 

from typing import List, Tuple 

 

import numpy as np 

import spglib 

 

from ase import Atoms 

from _icet import PermutationMatrix 

from icet.core.lattice_site import LatticeSite 

from icet.core.neighbor_list import NeighborList 

from icet.core.structure import Structure 

from icet.io.logging import logger 

from icet.tools.geometry import (ase_atoms_to_spglib_cell, 

get_fractional_positions_from_neighbor_list, 

get_primitive_structure) 

 

logger = logger.getChild('permutation_matrix') 

 

 

def permutation_matrix_from_structure(structure: Atoms, cutoff: float, 

find_prim: bool = True) \ 

-> Tuple[np.ndarray, Structure, NeighborList]: 

"""Sets up a list of permutation maps from an Atoms object. 

 

Parameters 

---------- 

structure 

input structure 

cutoff 

cutoff radius 

find_primitive 

if True the symmetries of the primitive structure will be employed 

 

Returns 

------- 

the tuple that is returned comprises the permutation matrix, the 

primitive structure, and the neighbor list 

""" 

 

structure = structure.copy() 

 

structure_prim = structure 

if find_prim: 

structure_prim = get_primitive_structure(structure) 

 

logger.debug('Size of primitive structure: {}'.format(len(structure_prim))) 

 

structure_as_tuple = ase_atoms_to_spglib_cell(structure_prim) 

 

# get symmetry information 

symmetry = spglib.get_symmetry(structure_as_tuple) 

translations = symmetry['translations'] 

rotations = symmetry['rotations'] 

 

# set up a permutation map object 

permutation_matrix = PermutationMatrix(translations, rotations) 

 

# create neighbor_lists from the different cutoffs 

prim_icet_structure = Structure.from_atoms(structure_prim) 

neighbor_list = NeighborList(cutoff) 

neighbor_list.build(prim_icet_structure) 

 

# get fractional positions for neighbor_list 

frac_positions = get_fractional_positions_from_neighbor_list( 

prim_icet_structure, neighbor_list) 

 

logger.debug('Number of fractional positions:' 

' {}'.format(len(frac_positions))) 

73 ↛ 76line 73 didn't jump to line 76, because the condition on line 73 was never false if frac_positions is not None: 

permutation_matrix.build(frac_positions) 

 

return permutation_matrix, prim_icet_structure, neighbor_list 

 

 

def _get_lattice_site_permutation_matrix(structure: Structure, 

permutation_matrix: PermutationMatrix, 

prune: bool = True) -> np.ndarray: 

""" 

Returns a transformed permutation matrix with lattice sites as entries 

instead of fractional coordinates. 

 

Parameters 

---------- 

structure 

primitive atomic icet structure 

permutation_matrix 

permutation matrix with fractional coordinates format entries 

prune 

if True the permutation matrix will be pruned 

 

Returns 

------- 

Permutation matrix in a row major order with lattice site format entries 

""" 

pm_frac = permutation_matrix.get_permuted_positions() 

 

pm_lattice_sites = [] 

for row in pm_frac: 

positions = _fractional_to_cartesian(row, structure.cell) 

lat_neighbors = [] 

if np.all(structure.pbc): 

lat_neighbors = \ 

structure.find_lattice_sites_by_positions(positions) 

else: 

for pos in positions: 

try: 

lat_neighbor = \ 

structure.find_lattice_site_by_position(pos) 

except RuntimeError: 

continue 

lat_neighbors.append(lat_neighbor) 

116 ↛ 119line 116 didn't jump to line 119, because the condition on line 116 was never false if lat_neighbors is not None: 

pm_lattice_sites.append(lat_neighbors) 

else: 

logger.warning('Unable to transform any element in a column of the' 

' fractional permutation matrix to lattice site') 

121 ↛ 130line 121 didn't jump to line 130, because the condition on line 121 was never false if prune: 

logger.debug('Size of columns of the permutation matrix before' 

' pruning {}'.format(len(pm_lattice_sites))) 

 

pm_lattice_sites = _prune_permutation_matrix(pm_lattice_sites) 

 

logger.debug('Size of columns of the permutation matrix after' 

' pruning {}'.format(len(pm_lattice_sites))) 

 

return pm_lattice_sites 

 

 

def _prune_permutation_matrix(permutation_matrix: List[List[LatticeSite]]): 

""" 

Prunes the matrix so that the first column only contains unique elements. 

 

Parameters 

---------- 

permutation_matrix 

permutation matrix with LatticeSite type entries 

""" 

 

for i in range(len(permutation_matrix)): 

for j in reversed(range(len(permutation_matrix))): 

if j <= i: 

continue 

if permutation_matrix[i][0] == permutation_matrix[j][0]: 

permutation_matrix.pop(j) 

logger.debug('Removing duplicate in permutation matrix' 

'i: {} j: {}'.format(i, j)) 

 

return permutation_matrix 

 

 

def _fractional_to_cartesian(fractional_coordinates: List[List[float]], 

cell: np.ndarray) -> List[float]: 

""" 

Converts cell metrics from fractional to cartesian coordinates. 

 

Parameters 

---------- 

fractional_coordinates 

list of fractional coordinates 

 

cell 

cell metric 

""" 

cartesian_coordinates = [np.dot(frac, cell) 

for frac in fractional_coordinates] 

return cartesian_coordinates