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
1import numpy as np
3from ase.parallel import world, broadcast
4from ase.geometry import get_distances
7def random_unit_vector(rng):
8 """Random unit vector equally distributed on the sphere
10 Parameter
11 ---------
12 rng: random number generator object
13 """
14 ct = -1 + 2 * rng.rand()
15 phi = 2 * np.pi * rng.rand()
16 st = np.sqrt(1 - ct**2)
17 return np.array([st * np.cos(phi), st * np.sin(phi), ct])
20def nearest(atoms1, atoms2, cell=None, pbc=None):
21 """Return indices of nearest atoms"""
22 p1 = atoms1.get_positions()
23 p2 = atoms2.get_positions()
24 vd_aac, d2_aa = get_distances(p1, p2, cell, pbc)
25 i1, i2 = np.argwhere(d2_aa == d2_aa.min())[0]
26 return i1, i2, vd_aac[i1, i2]
29def attach(atoms1, atoms2, distance, direction=(1, 0, 0),
30 maxiter=50, accuracy=1e-5):
31 """Attach two structures
33 Parameters
34 ----------
35 atoms1: Atoms
36 cell and pbc of this object are used
37 atoms2: Atoms
38 distance: float
39 minimal distance (Angstrom)
40 direction: unit vector (3 floats)
41 relative direction between center of masses
42 maxiter: int
43 maximal number of iterations to get required distance, default 100
44 accuracy: float
45 required accuracy for minimal distance (Angstrom), default 1e-5
46 """
47 atoms = atoms1.copy()
48 atoms2 = atoms2.copy()
50 direction = np.array(direction, dtype=float)
51 direction /= np.linalg.norm(direction)
52 assert len(direction) == 3
53 dist2 = distance**2
55 i1, i2, dv_c = nearest(atoms, atoms2, atoms.cell, atoms.pbc)
57 for i in range(maxiter):
58 dv2 = (dv_c**2).sum()
60 vcost = np.dot(dv_c, direction)
61 a = np.sqrt(max(0, dist2 - dv2 + vcost**2))
62 move = a - vcost
63 if abs(move) < accuracy:
64 atoms += atoms2
65 return atoms
67 # we need to move
68 atoms2.translate(direction * move)
69 i1, i2, dv_c = nearest(atoms, atoms2, atoms.cell, atoms.pbc)
71 raise RuntimeError('attach did not converge')
74def attach_randomly(atoms1, atoms2, distance,
75 rng=np.random):
76 """Randomly attach two structures with a given minimal distance
78 Parameters
79 ----------
80 atoms1: Atoms object
81 atoms2: Atoms object
82 distance: float
83 Required distance
84 rng: random number generator object
85 defaults to np.random.RandomState()
87 Returns
88 -------
89 Joined structure as an atoms object.
90 """
91 atoms2 = atoms2.copy()
92 atoms2.rotate('x', random_unit_vector(rng),
93 center=atoms2.get_center_of_mass())
94 return attach(atoms1, atoms2, distance,
95 direction=random_unit_vector(rng))
98def attach_randomly_and_broadcast(atoms1, atoms2, distance,
99 rng=np.random,
100 comm=world):
101 """Randomly attach two structures with a given minimal distance
102 and ensure that these are distributed.
104 Parameters
105 ----------
106 atoms1: Atoms object
107 atoms2: Atoms object
108 distance: float
109 Required distance
110 rng: random number generator object
111 defaults to np.random.RandomState()
112 comm: communicator to distribute
113 Communicator to distribute the structure, default: world
115 Returns
116 -------
117 Joined structure as an atoms object.
118 """
119 if comm.rank == 0:
120 joined = attach_randomly(atoms1, atoms2, distance, rng)
121 broadcast(joined, 0, comm=comm)
122 else:
123 joined = broadcast(None, 0, comm)
124 return joined