Coverage for /builds/ase/ase/ase/cluster/icosahedron.py: 98.25%

57 statements  

« prev     ^ index     » next       coverage.py v7.5.3, created at 2025-08-02 00:12 +0000

1# fmt: off 

2 

3import numpy as np 

4 

5from ase import Atoms 

6from ase.cluster.util import get_element_info 

7 

8 

9def Icosahedron(symbol, noshells, latticeconstant=None): 

10 """ 

11 Returns a cluster with the icosahedra symmetry. 

12 

13 Parameters 

14 ---------- 

15 symbol : str or int 

16 The chemical symbol (or atomic number) of the element. 

17 

18 noshells : int 

19 The number of shells (>= 1). 

20 

21 latticeconstant : float, optional 

22 The lattice constant. If not given, then it is extracted from 

23 `ase.data`. 

24 """ 

25 

26 symbol, atomic_number, latticeconstant = get_element_info( 

27 symbol, latticeconstant) 

28 

29 # Interpret noshells 

30 if noshells < 1: 

31 raise ValueError( 

32 "The number of shells must be equal to or greater than one.") 

33 

34 t = 0.5 + np.sqrt(5) / 2.0 

35 

36 verticies = np.array([[t, 0., 1.], 

37 [t, 0., -1.], 

38 [-t, 0., 1.], 

39 [-t, 0., -1.], 

40 [1., t, 0.], 

41 [-1., t, 0.], 

42 [1., -t, 0.], 

43 [-1., -t, 0.], 

44 [0., 1., t], 

45 [0., -1., t], 

46 [0., 1., -t], 

47 [0., -1., -t]]) 

48 

49 positions = [] 

50 tags = [] 

51 positions.append(np.zeros(3)) 

52 tags.append(1) 

53 

54 for n in range(1, noshells): 

55 # Construct square edges (6) 

56 for k in range(0, 12, 2): 

57 v1 = verticies[k] 

58 v2 = verticies[k + 1] 

59 for i in range(n + 1): 

60 pos = i * v1 + (n - i) * v2 

61 positions.append(pos) 

62 tags.append(n + 1) 

63 

64 # Construct triangle planes (12) 

65 if n > 1: 

66 map = {0: (8, 9), 1: (10, 11), 

67 2: (8, 9), 3: (10, 11), 

68 4: (0, 1), 5: (2, 3), 

69 6: (0, 1), 7: (2, 3), 

70 8: (4, 5), 9: (6, 7), 

71 10: (4, 5), 11: (6, 7)} 

72 

73 for k in range(12): 

74 v0 = n * verticies[k] 

75 v1 = (verticies[map[k][0]] - verticies[k]) 

76 v2 = (verticies[map[k][1]] - verticies[k]) 

77 for i in range(n): 

78 for j in range(n - i): 

79 if i == 0 and j == 0: 

80 continue 

81 pos = v0 + i * v1 + j * v2 

82 positions.append(pos) 

83 tags.append(n + 1) 

84 

85 # Fill missing triangle planes (8) 

86 if n > 2: 

87 map = {0: (9, 6, 8, 4,), 

88 1: (11, 6, 10, 4), 

89 2: (9, 7, 8, 5,), 

90 3: (11, 7, 10, 5)} 

91 

92 for k in range(4): 

93 v0 = n * verticies[k] 

94 v1 = (verticies[map[k][0]] - verticies[k]) 

95 v2 = (verticies[map[k][1]] - verticies[k]) 

96 v3 = (verticies[map[k][2]] - verticies[k]) 

97 v4 = (verticies[map[k][3]] - verticies[k]) 

98 for i in range(1, n): 

99 for j in range(1, n - i): 

100 pos = v0 + i * v1 + j * v2 

101 positions.append(pos) 

102 tags.append(n + 1) 

103 pos = v0 + i * v3 + j * v4 

104 positions.append(pos) 

105 tags.append(n + 1) 

106 

107 # Scale the positions 

108 scaling_factor = latticeconstant / np.sqrt(2 * (1 + t**2)) 

109 positions = np.array(positions) * scaling_factor 

110 

111 symbols = [atomic_number] * len(positions) 

112 atoms = Atoms(symbols=symbols, positions=positions, tags=tags) 

113 atoms.center(about=(0, 0, 0)) 

114 atoms.cell[:] = 0 

115 return atoms