Coverage for /builds/kinetik161/ase/ase/geometry/distance.py: 100.00%

40 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-12-10 11:04 +0000

1import numpy as np 

2 

3 

4def distance(s1, s2, permute=True): 

5 """Get the distance between two structures s1 and s2. 

6 

7 The distance is defined by the Frobenius norm of 

8 the spatial distance between all coordinates (see 

9 numpy.linalg.norm for the definition). 

10 

11 permute: minimise the distance by 'permuting' same elements 

12 """ 

13 

14 s1 = s1.copy() 

15 s2 = s2.copy() 

16 for s in [s1, s2]: 

17 s.translate(-s.get_center_of_mass()) 

18 s2pos = 1. * s2.get_positions() 

19 

20 def align(struct, xaxis='x', yaxis='y'): 

21 """Align moments of inertia with the coordinate system.""" 

22 Is, Vs = struct.get_moments_of_inertia(True) 

23 IV = list(zip(Is, Vs)) 

24 IV.sort(key=lambda x: x[0]) 

25 struct.rotate(IV[0][1], xaxis) 

26 

27 Is, Vs = struct.get_moments_of_inertia(True) 

28 IV = list(zip(Is, Vs)) 

29 IV.sort(key=lambda x: x[0]) 

30 struct.rotate(IV[1][1], yaxis) 

31 

32 align(s1) 

33 

34 def dd(s1, s2, permute): 

35 if permute: 

36 s2 = s2.copy() 

37 dist = 0 

38 for a in s1: 

39 imin = None 

40 dmin = np.Inf 

41 for i, b in enumerate(s2): 

42 if a.symbol == b.symbol: 

43 d = np.sum((a.position - b.position)**2) 

44 if d < dmin: 

45 dmin = d 

46 imin = i 

47 dist += dmin 

48 s2.pop(imin) 

49 return np.sqrt(dist) 

50 else: 

51 return np.linalg.norm(s1.get_positions() - s2.get_positions()) 

52 

53 dists = [] 

54 # principles 

55 for x, y in zip(['x', '-x', 'x', '-x'], ['y', 'y', '-y', '-y']): 

56 s2.set_positions(s2pos) 

57 align(s2, x, y) 

58 dists.append(dd(s1, s2, permute)) 

59 

60 return min(dists)