Coverage for /builds/ase/ase/ase/cluster/cluster.py: 41.18%

51 statements  

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

1# fmt: off 

2 

3import math 

4 

5import numpy as np 

6 

7from ase import Atoms 

8from ase.cluster.base import ClusterBase 

9 

10 

11class Cluster(Atoms, ClusterBase): 

12 symmetry = None 

13 surfaces = None 

14 lattice_basis = None 

15 resiproc_basis = None 

16 atomic_basis = None 

17 

18 def copy(self): 

19 cluster = Atoms.copy(self) 

20 cluster.symmetry = self.symmetry 

21 cluster.surfaces = self.surfaces.copy() 

22 cluster.lattice_basis = self.lattice_basis.copy() 

23 cluster.atomic_basis = self.atomic_basis.copy() 

24 cluster.resiproc_basis = self.resiproc_basis.copy() 

25 return cluster 

26 

27 def get_surfaces(self): 

28 """Returns the miller indexs of the stored surfaces of the cluster.""" 

29 if self.surfaces is not None: 

30 return self.surfaces.copy() 

31 else: 

32 return None 

33 

34 def get_layers(self): 

35 """Return number of atomic layers in stored surfaces directions.""" 

36 

37 layers = [] 

38 

39 for s in self.surfaces: 

40 n = self.miller_to_direction(s) 

41 c = self.get_positions().mean(axis=0) 

42 r = np.dot(self.get_positions() - c, n).max() 

43 d = self.get_layer_distance(s, 2) 

44 l_ = 2 * np.round(r / d).astype(int) 

45 

46 ls = np.arange(l_ - 1, l_ + 2) 

47 ds = np.array([self.get_layer_distance(s, i) for i in ls]) 

48 

49 mask = (np.abs(ds - r) < 1e-10) 

50 

51 layers.append(ls[mask][0]) 

52 

53 return np.array(layers, int) 

54 

55 def get_diameter(self, method='volume'): 

56 """Returns an estimate of the cluster diameter based on two different 

57 methods. 

58 

59 Parameters 

60 ---------- 

61 method : {'volume', 'shape'} 

62 'volume' (default) returns the diameter of a sphere with the same 

63 volume as the atoms. 'shape' returns the averaged diameter 

64 calculated from the directions given by the defined surfaces. 

65 """ 

66 

67 if method == 'shape': 

68 cen = self.get_positions().mean(axis=0) 

69 pos = self.get_positions() - cen 

70 d = 0.0 

71 for s in self.surfaces: 

72 n = self.miller_to_direction(s) 

73 r = np.dot(pos, n) 

74 d += r.max() - r.min() 

75 return d / len(self.surfaces) 

76 elif method == 'volume': 

77 V_cell = np.abs(np.linalg.det(self.lattice_basis)) 

78 N_cell = len(self.atomic_basis) 

79 N = len(self) 

80 return 2.0 * (3.0 * N * V_cell / 

81 (4.0 * math.pi * N_cell)) ** (1.0 / 3.0) 

82 else: 

83 return 0.0