Coverage for /builds/ase/ase/ase/geometry/distance.py: 100.00%
40 statements
« prev ^ index » next coverage.py v7.5.3, created at 2025-08-02 00:12 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2025-08-02 00:12 +0000
1# fmt: off
3import numpy as np
6def distance(s1, s2, permute=True):
7 """Get the distance between two structures s1 and s2.
9 The distance is defined by the Frobenius norm of
10 the spatial distance between all coordinates (see
11 numpy.linalg.norm for the definition).
13 permute: minimise the distance by 'permuting' same elements
14 """
16 s1 = s1.copy()
17 s2 = s2.copy()
18 for s in [s1, s2]:
19 s.translate(-s.get_center_of_mass())
20 s2pos = 1. * s2.get_positions()
22 def align(struct, xaxis='x', yaxis='y'):
23 """Align moments of inertia with the coordinate system."""
24 Is, Vs = struct.get_moments_of_inertia(True)
25 IV = list(zip(Is, Vs))
26 IV.sort(key=lambda x: x[0])
27 struct.rotate(IV[0][1], xaxis)
29 Is, Vs = struct.get_moments_of_inertia(True)
30 IV = list(zip(Is, Vs))
31 IV.sort(key=lambda x: x[0])
32 struct.rotate(IV[1][1], yaxis)
34 align(s1)
36 def dd(s1, s2, permute):
37 if permute:
38 s2 = s2.copy()
39 dist = 0
40 for a in s1:
41 imin = None
42 dmin = np.inf
43 for i, b in enumerate(s2):
44 if a.symbol == b.symbol:
45 d = np.sum((a.position - b.position)**2)
46 if d < dmin:
47 dmin = d
48 imin = i
49 dist += dmin
50 s2.pop(imin)
51 return np.sqrt(dist)
52 else:
53 return np.linalg.norm(s1.get_positions() - s2.get_positions())
55 dists = []
56 # principles
57 for x, y in zip(['x', '-x', 'x', '-x'], ['y', 'y', '-y', '-y']):
58 s2.set_positions(s2pos)
59 align(s2, x, y)
60 dists.append(dd(s1, s2, permute))
62 return min(dists)