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
4delta = 1e-10
7def wulff_construction(symbol, surfaces, energies, size, structure,
8 rounding='closest', latticeconstant=None,
9 debug=False, maxiter=100):
10 """Create a cluster using the Wulff construction.
12 A cluster is created with approximately the number of atoms
13 specified, following the Wulff construction, i.e. minimizing the
14 surface energy of the cluster.
16 Parameters:
18 symbol: The chemical symbol (or atomic number) of the desired element.
20 surfaces: A list of surfaces. Each surface is an (h, k, l) tuple or
21 list of integers.
23 energies: A list of surface energies for the surfaces.
25 size: The desired number of atoms.
27 structure: The desired crystal structure. One of the strings
28 "fcc", "bcc", or "sc".
30 rounding (optional): Specifies what should be done if no Wulff
31 construction corresponds to exactly the requested number of atoms.
32 Should be a string, either "above", "below" or "closest" (the
33 default), meaning that the nearest cluster above or below - or the
34 closest one - is created instead.
36 latticeconstant (optional): The lattice constant. If not given,
37 extracted from ase.data.
39 debug (optional): If non-zero, information about the iteration towards
40 the right cluster size is printed.
41 """
43 if debug:
44 print('Wulff: Aiming for cluster with %i atoms (%s)' %
45 (size, rounding))
47 if rounding not in ['above', 'below', 'closest']:
48 raise ValueError('Invalid rounding: %s' % rounding)
50 # Interpret structure, if it is a string.
51 if isinstance(structure, str):
52 if structure == 'fcc':
53 from ase.cluster.cubic import FaceCenteredCubic as structure
54 elif structure == 'bcc':
55 from ase.cluster.cubic import BodyCenteredCubic as structure
56 elif structure == 'sc':
57 from ase.cluster.cubic import SimpleCubic as structure
58 elif structure == 'hcp':
59 from ase.cluster.hexagonal import \
60 HexagonalClosedPacked as structure
61 elif structure == 'graphite':
62 from ase.cluster.hexagonal import Graphite as structure
63 else:
64 error = 'Crystal structure %s is not supported.' % structure
65 raise NotImplementedError(error)
67 # Check number of surfaces
68 nsurf = len(surfaces)
69 if len(energies) != nsurf:
70 raise ValueError('The energies array should contain %d values.'
71 % (nsurf,))
73 # Copy energies array so it is safe to modify it
74 energies = np.array(energies)
76 # We should check that for each direction, the surface energy plus
77 # the energy in the opposite direction is positive. But this is
78 # very difficult in the general case!
80 # Before starting, make a fake cluster just to extract the
81 # interlayer distances in the relevant directions, and use these
82 # to "renormalize" the surface energies such that they can be used
83 # to convert to number of layers instead of to distances.
84 atoms = structure(symbol, surfaces, 5 * np.ones(len(surfaces), int),
85 latticeconstant=latticeconstant)
86 for i, s in enumerate(surfaces):
87 d = atoms.get_layer_distance(s)
88 energies[i] /= d
90 # First guess a size that is not too large.
91 wanted_size = size ** (1.0 / 3.0)
92 max_e = max(energies)
93 factor = wanted_size / max_e
94 atoms, layers = make_atoms(symbol, surfaces, energies, factor, structure,
95 latticeconstant)
96 if len(atoms) == 0:
97 # Probably the cluster is very flat
98 if debug:
99 print('First try made an empty cluster, trying again.')
100 factor = 1 / energies.min()
101 atoms, layers = make_atoms(symbol, surfaces, energies, factor,
102 structure, latticeconstant)
103 if len(atoms) == 0:
104 raise RuntimeError('Failed to create a finite cluster.')
106 # Second guess: scale to get closer.
107 old_factor = factor
108 old_layers = layers
109 old_atoms = atoms
110 factor *= (size / len(atoms))**(1.0 / 3.0)
111 atoms, layers = make_atoms(symbol, surfaces, energies, factor,
112 structure, latticeconstant)
113 if len(atoms) == 0:
114 print('Second guess gave an empty cluster, discarding it.')
115 atoms = old_atoms
116 factor = old_factor
117 layers = old_layers
118 else:
119 del old_atoms
121 # Find if the cluster is too small or too large (both means perfect!)
122 below = above = None
123 if len(atoms) <= size:
124 below = atoms
125 if len(atoms) >= size:
126 above = atoms
128 # Now iterate towards the right cluster
129 iter = 0
130 while (below is None or above is None):
131 if len(atoms) < size:
132 # Find a larger cluster
133 if debug:
134 print('Making a larger cluster.')
135 factor = ((layers + 0.5 + delta) / energies).min()
136 atoms, new_layers = make_atoms(symbol, surfaces, energies, factor,
137 structure, latticeconstant)
138 assert (new_layers - layers).max() == 1
139 assert (new_layers - layers).min() >= 0
140 layers = new_layers
141 else:
142 # Find a smaller cluster
143 if debug:
144 print('Making a smaller cluster.')
145 factor = ((layers - 0.5 - delta) / energies).max()
146 atoms, new_layers = make_atoms(symbol, surfaces, energies, factor,
147 structure, latticeconstant)
148 assert (new_layers - layers).max() <= 0
149 assert (new_layers - layers).min() == -1
150 layers = new_layers
151 if len(atoms) <= size:
152 below = atoms
153 if len(atoms) >= size:
154 above = atoms
155 iter += 1
156 if iter == maxiter:
157 raise RuntimeError('Runaway iteration.')
158 if rounding == 'below':
159 if debug:
160 print('Choosing smaller cluster with %i atoms' % len(below))
161 return below
162 elif rounding == 'above':
163 if debug:
164 print('Choosing larger cluster with %i atoms' % len(above))
165 return above
166 else:
167 assert rounding == 'closest'
168 if (len(above) - size) < (size - len(below)):
169 atoms = above
170 else:
171 atoms = below
172 if debug:
173 print('Choosing closest cluster with %i atoms' % len(atoms))
174 return atoms
177def make_atoms(symbol, surfaces, energies, factor, structure, latticeconstant):
178 layers1 = factor * np.array(energies)
179 layers = np.round(layers1).astype(int)
180 atoms = structure(symbol, surfaces, layers,
181 latticeconstant=latticeconstant)
182 return (atoms, layers)