Statistiques
| Révision :

root / ase / lattice / surface.py @ 20

Historique | Voir | Annoter | Télécharger (10,52 ko)

1 1 tkerber
"""Helper functions for creating the most common surfaces and related tasks.
2 1 tkerber

3 1 tkerber
The helper functions can create the most common low-index surfaces,
4 1 tkerber
add vacuum layers and add adsorbates.
5 1 tkerber

6 1 tkerber
"""
7 1 tkerber
8 1 tkerber
from math import sqrt
9 1 tkerber
10 1 tkerber
import numpy as np
11 1 tkerber
12 1 tkerber
from ase.atom import Atom
13 1 tkerber
from ase.atoms import Atoms
14 1 tkerber
from ase.data import reference_states, atomic_numbers
15 1 tkerber
16 1 tkerber
17 1 tkerber
def fcc100(symbol, size, a=None, vacuum=None):
18 1 tkerber
    """FCC(100) surface.
19 1 tkerber

20 1 tkerber
    Supported special adsorption sites: 'ontop', 'bridge', 'hollow'."""
21 1 tkerber
    return surface(symbol, 'fcc', '100', size, a, None, vacuum)
22 1 tkerber
23 1 tkerber
def fcc110(symbol, size, a=None, vacuum=None):
24 1 tkerber
    """FCC(110) surface.
25 1 tkerber

26 1 tkerber
    Supported special adsorption sites: 'ontop', 'longbridge',
27 1 tkerber
    'shortbridge','hollow'."""
28 1 tkerber
    return surface(symbol, 'fcc', '110', size, a, None, vacuum)
29 1 tkerber
30 1 tkerber
def bcc100(symbol, size, a=None, vacuum=None):
31 1 tkerber
    """BCC(100) surface.
32 1 tkerber

33 1 tkerber
    Supported special adsorption sites: 'ontop', 'bridge', 'hollow'."""
34 1 tkerber
    return surface(symbol, 'bcc', '100', size, a, None, vacuum)
35 1 tkerber
36 1 tkerber
def bcc110(symbol, size, a=None, vacuum=None, orthogonal=False):
37 1 tkerber
    """BCC(110) surface.
38 1 tkerber

39 1 tkerber
    Supported special adsorption sites: 'ontop', 'longbridge',
40 1 tkerber
    'shortbridge', 'hollow'.
41 1 tkerber

42 1 tkerber
    Use *orthogonal=True* to get an orthogonal unit cell - works only
43 1 tkerber
    for size=(i,j,k) with j even."""
44 1 tkerber
    return surface(symbol, 'bcc', '110', size, a, None, vacuum, orthogonal)
45 1 tkerber
46 1 tkerber
def bcc111(symbol, size, a=None, vacuum=None, orthogonal=False):
47 1 tkerber
    """BCC(111) surface.
48 1 tkerber

49 1 tkerber
    Supported special adsorption sites: 'ontop'.
50 1 tkerber

51 1 tkerber
    Use *orthogonal=True* to get an orthogonal unit cell - works only
52 1 tkerber
    for size=(i,j,k) with j even."""
53 1 tkerber
    return surface(symbol, 'bcc', '111', size, a, None, vacuum, orthogonal)
54 1 tkerber
55 1 tkerber
def fcc111(symbol, size, a=None, vacuum=None, orthogonal=False):
56 1 tkerber
    """FCC(111) surface.
57 1 tkerber

58 1 tkerber
    Supported special adsorption sites: 'ontop', 'bridge', 'fcc' and 'hcp'.
59 1 tkerber

60 1 tkerber
    Use *orthogonal=True* to get an orthogonal unit cell - works only
61 1 tkerber
    for size=(i,j,k) with j even."""
62 1 tkerber
    return surface(symbol, 'fcc', '111', size, a, None, vacuum, orthogonal)
63 1 tkerber
64 1 tkerber
def hcp0001(symbol, size, a=None, c=None, vacuum=None, orthogonal=False):
65 1 tkerber
    """HCP(0001) surface.
66 1 tkerber

67 1 tkerber
    Supported special adsorption sites: 'ontop', 'bridge', 'fcc' and 'hcp'.
68 1 tkerber

69 1 tkerber
    Use *orthogonal=True* to get an orthogonal unit cell - works only
70 1 tkerber
    for size=(i,j,k) with j even."""
71 1 tkerber
    return surface(symbol, 'hcp', '0001', size, a, c, vacuum, orthogonal)
72 1 tkerber
73 1 tkerber
74 1 tkerber
def add_adsorbate(slab, adsorbate, height, position=(0, 0), offset=None,
75 1 tkerber
                  mol_index=0):
76 1 tkerber
    """Add an adsorbate to a surface.
77 1 tkerber

78 1 tkerber
    This function adds an adsorbate to a slab.  If the slab is
79 1 tkerber
    produced by one of the utility functions in ase.lattice.surface, it
80 1 tkerber
    is possible to specify the position of the adsorbate by a keyword
81 1 tkerber
    (the supported keywords depend on which function was used to
82 1 tkerber
    create the slab).
83 1 tkerber

84 1 tkerber
    If the adsorbate is a molecule, the atom indexed by the mol_index
85 1 tkerber
    optional argument is positioned on top of the adsorption position
86 1 tkerber
    on the surface, and it is the responsibility of the user to orient
87 1 tkerber
    the adsorbate in a sensible way.
88 1 tkerber

89 1 tkerber
    This function can be called multiple times to add more than one
90 1 tkerber
    adsorbate.
91 1 tkerber

92 1 tkerber
    Parameters:
93 1 tkerber

94 1 tkerber
    slab: The surface onto which the adsorbate should be added.
95 1 tkerber

96 1 tkerber
    adsorbate:  The adsorbate. Must be one of the following three types:
97 1 tkerber
        A string containing the chemical symbol for a single atom.
98 1 tkerber
        An atom object.
99 1 tkerber
        An atoms object (for a molecular adsorbate).
100 1 tkerber

101 1 tkerber
    height: Height above the surface.
102 1 tkerber

103 1 tkerber
    position: The x-y position of the adsorbate, either as a tuple of
104 1 tkerber
        two numbers or as a keyword (if the surface is produced by one
105 1 tkerber
        of the functions in ase.lattice.surfaces).
106 1 tkerber

107 1 tkerber
    offset (default: None): Offsets the adsorbate by a number of unit
108 1 tkerber
        cells. Mostly useful when adding more than one adsorbate.
109 1 tkerber

110 1 tkerber
    mol_index (default: 0): If the adsorbate is a molecule, index of
111 1 tkerber
        the atom to be positioned above the location specified by the
112 1 tkerber
        position argument.
113 1 tkerber

114 1 tkerber
    Note *position* is given in absolute xy coordinates (or as
115 1 tkerber
    a keyword), whereas offset is specified in unit cells.  This
116 1 tkerber
    can be used to give the positions in units of the unit cell by
117 1 tkerber
    using *offset* instead.
118 1 tkerber

119 1 tkerber
    """
120 1 tkerber
    info = slab.adsorbate_info
121 1 tkerber
    if 'cell' not in info:
122 1 tkerber
        info['cell'] = slab.get_cell()[:2,:2]
123 1 tkerber
124 1 tkerber
125 1 tkerber
    pos = np.array([0.0, 0.0])  # (x, y) part
126 1 tkerber
    spos = np.array([0.0, 0.0]) # part relative to unit cell
127 1 tkerber
    if offset is not None:
128 1 tkerber
        spos += np.asarray(offset, float)
129 1 tkerber
130 1 tkerber
    if isinstance(position, str):
131 1 tkerber
        # A site-name:
132 1 tkerber
        if 'sites' not in info:
133 1 tkerber
            raise TypeError('If the atoms are not made by an ' +
134 1 tkerber
                            'ase.lattice.surface function, ' +
135 1 tkerber
                            'position cannot be a name.')
136 1 tkerber
        if position not in info['sites']:
137 1 tkerber
            raise TypeError('Adsorption site %s not supported.' % position)
138 1 tkerber
        spos += info['sites'][position]
139 1 tkerber
    else:
140 1 tkerber
        pos += position
141 1 tkerber
142 1 tkerber
    pos += np.dot(spos, info['cell'])
143 1 tkerber
144 1 tkerber
    # Convert the adsorbate to an Atoms object
145 1 tkerber
    if isinstance(adsorbate, Atoms):
146 1 tkerber
        ads = adsorbate
147 1 tkerber
    elif isinstance(adsorbate, Atom):
148 1 tkerber
        ads = Atoms([adsorbate])
149 1 tkerber
    else:
150 1 tkerber
        # Hope it is a useful string or something like that
151 1 tkerber
        ads = Atoms(adsorbate)
152 1 tkerber
153 1 tkerber
    # Get the z-coordinate:
154 1 tkerber
    try:
155 1 tkerber
        a = info['top layer atom index']
156 1 tkerber
    except KeyError:
157 1 tkerber
        a = slab.positions[:, 2].argmax()
158 1 tkerber
        info['top layer atom index']= a
159 1 tkerber
    z = slab.positions[a, 2] + height
160 1 tkerber
161 1 tkerber
    # Move adsorbate into position
162 1 tkerber
    ads.translate([pos[0], pos[1], z] - ads.positions[mol_index])
163 1 tkerber
164 1 tkerber
    # Attach the adsorbate
165 1 tkerber
    slab.extend(ads)
166 1 tkerber
167 1 tkerber
168 1 tkerber
def surface(symbol, structure, face, size, a, c, vacuum, orthogonal=True):
169 1 tkerber
    """Function to build often used surfaces.
170 1 tkerber

171 1 tkerber
    Don't call this function directly - use fcc100, fcc110, bcc111, ..."""
172 1 tkerber
173 1 tkerber
    Z = atomic_numbers[symbol]
174 1 tkerber
175 1 tkerber
    if a is None:
176 1 tkerber
        sym = reference_states[Z]['symmetry'].lower()
177 1 tkerber
        if sym != structure:
178 1 tkerber
            raise ValueError("Can't guess lattice constant for %s-%s!" %
179 1 tkerber
                             (structure, symbol))
180 1 tkerber
        a = reference_states[Z]['a']
181 1 tkerber
182 1 tkerber
    if structure == 'hcp' and c is None:
183 1 tkerber
        if reference_states[Z]['symmetry'].lower() == 'hcp':
184 1 tkerber
            c = reference_states[Z]['c/a'] * a
185 1 tkerber
        else:
186 1 tkerber
            c = sqrt(8 / 3.0) * a
187 1 tkerber
188 1 tkerber
    positions = np.empty((size[2], size[1], size[0], 3))
189 1 tkerber
    positions[..., 0] = np.arange(size[0]).reshape((1, 1, -1))
190 1 tkerber
    positions[..., 1] = np.arange(size[1]).reshape((1, -1, 1))
191 1 tkerber
    positions[..., 2] = np.arange(size[2]).reshape((-1, 1, 1))
192 1 tkerber
193 1 tkerber
    numbers = np.ones(size[0] * size[1] * size[2], int) * Z
194 1 tkerber
195 1 tkerber
    tags = np.empty((size[2], size[1], size[0]), int)
196 1 tkerber
    tags[:] = np.arange(size[2], 0, -1).reshape((-1, 1, 1))
197 1 tkerber
198 1 tkerber
    slab = Atoms(numbers,
199 1 tkerber
                 tags=tags.ravel(),
200 1 tkerber
                 pbc=(True, True, False),
201 1 tkerber
                 cell=size)
202 1 tkerber
203 1 tkerber
    surface_cell = None
204 1 tkerber
    sites = {'ontop': (0, 0)}
205 1 tkerber
    surf = structure + face
206 1 tkerber
    if surf == 'fcc100':
207 1 tkerber
        cell = (sqrt(0.5), sqrt(0.5), 0.5)
208 1 tkerber
        positions[-2::-2, ..., :2] += 0.5
209 1 tkerber
        sites.update({'hollow': (0.5, 0.5), 'bridge': (0.5, 0)})
210 1 tkerber
    elif surf == 'fcc110':
211 1 tkerber
        cell = (1.0, sqrt(0.5), sqrt(0.125))
212 1 tkerber
        positions[-2::-2, ..., :2] += 0.5
213 1 tkerber
        sites.update({'hollow': (0.5, 0.5), 'longbridge': (0.5, 0),
214 1 tkerber
                      'shortbridge': (0, 0.5)})
215 1 tkerber
    elif surf == 'bcc100':
216 1 tkerber
        cell = (1.0, 1.0, 0.5)
217 1 tkerber
        positions[-2::-2, ..., :2] += 0.5
218 1 tkerber
        sites.update({'hollow': (0.5, 0.5), 'bridge': (0.5, 0)})
219 1 tkerber
    else:
220 1 tkerber
        if orthogonal and size[1] % 2 == 1:
221 1 tkerber
            raise ValueError(("Can't make orthorhombic cell with size=%r.  " %
222 1 tkerber
                              (tuple(size),)) +
223 1 tkerber
                             'Second number in size must be even.')
224 1 tkerber
        if surf == 'fcc111':
225 1 tkerber
            cell = (sqrt(0.5), sqrt(0.375), 1 / sqrt(3))
226 1 tkerber
            if orthogonal:
227 1 tkerber
                positions[-1::-3, 1::2, :, 0] += 0.5
228 1 tkerber
                positions[-2::-3, 1::2, :, 0] += 0.5
229 1 tkerber
                positions[-3::-3, 1::2, :, 0] -= 0.5
230 1 tkerber
                positions[-2::-3, ..., :2] += (0.0, 2.0 / 3)
231 1 tkerber
                positions[-3::-3, ..., :2] += (0.5, 1.0 / 3)
232 1 tkerber
            else:
233 1 tkerber
                positions[-2::-3, ..., :2] += (-1.0 / 3, 2.0 / 3)
234 1 tkerber
                positions[-3::-3, ..., :2] += (1.0 / 3, 1.0 / 3)
235 1 tkerber
            sites.update({'bridge': (0.5, 0), 'fcc': (1.0 / 3, 1.0 / 3),
236 1 tkerber
                          'hcp': (2.0 / 3, 2.0 / 3)})
237 1 tkerber
        elif surf == 'hcp0001':
238 1 tkerber
            cell = (1.0, sqrt(0.75), 0.5 * c / a)
239 1 tkerber
            if orthogonal:
240 1 tkerber
                positions[:, 1::2, :, 0] += 0.5
241 1 tkerber
                positions[-2::-2, ..., :2] += (0.0, 2.0 / 3)
242 1 tkerber
            else:
243 1 tkerber
                positions[-2::-2, ..., :2] += (-1.0 / 3, 2.0 / 3)
244 1 tkerber
            sites.update({'bridge': (0.5, 0), 'fcc': (1.0 / 3, 1.0 / 3),
245 1 tkerber
                          'hcp': (2.0 / 3, 2.0 / 3)})
246 1 tkerber
        elif surf == 'bcc110':
247 1 tkerber
            cell = (1.0, sqrt(0.5), sqrt(0.5))
248 1 tkerber
            if orthogonal:
249 1 tkerber
                positions[:, 1::2, :, 0] += 0.5
250 1 tkerber
                positions[-2::-2, ..., :2] += (0.0, 1.0)
251 1 tkerber
            else:
252 1 tkerber
                positions[-2::-2, ..., :2] += (-0.5, 1.0)
253 1 tkerber
            sites.update({'shortbridge': (0, 0.5),
254 1 tkerber
                          'longbridge': (0.5, 0),
255 1 tkerber
                          'hollow': (0.375, 0.25)})
256 1 tkerber
        elif surf == 'bcc111':
257 1 tkerber
            cell = (sqrt(2), sqrt(1.5), sqrt(3) / 6)
258 1 tkerber
            if orthogonal:
259 1 tkerber
                positions[-1::-3, 1::2, :, 0] += 0.5
260 1 tkerber
                positions[-2::-3, 1::2, :, 0] += 0.5
261 1 tkerber
                positions[-3::-3, 1::2, :, 0] -= 0.5
262 1 tkerber
                positions[-2::-3, ..., :2] += (0.0, 2.0 / 3)
263 1 tkerber
                positions[-3::-3, ..., :2] += (0.5, 1.0 / 3)
264 1 tkerber
            else:
265 1 tkerber
                positions[-2::-3, ..., :2] += (-1.0 / 3, 2.0 / 3)
266 1 tkerber
                positions[-3::-3, ..., :2] += (1.0 / 3, 1.0 / 3)
267 1 tkerber
            sites.update({'hollow': (1.0 / 3, 1.0 / 3)})
268 1 tkerber
269 1 tkerber
        surface_cell = a * np.array([(cell[0], 0),
270 1 tkerber
                                     (cell[0] / 2, cell[1])])
271 1 tkerber
        if not orthogonal:
272 1 tkerber
            cell = np.array([(cell[0], 0, 0),
273 1 tkerber
                             (cell[0] / 2, cell[1], 0),
274 1 tkerber
                             (0, 0, cell[2])])
275 1 tkerber
276 1 tkerber
    if surface_cell is None:
277 1 tkerber
        surface_cell = a * np.diag(cell[:2])
278 1 tkerber
279 1 tkerber
    if isinstance(cell, tuple):
280 1 tkerber
        cell = np.diag(cell)
281 1 tkerber
282 1 tkerber
    slab.set_positions(positions.reshape((-1, 3)))
283 1 tkerber
284 1 tkerber
    slab.set_cell([a * v * n for v, n in zip(cell, size)], scale_atoms=True)
285 1 tkerber
286 1 tkerber
    if vacuum is not None:
287 1 tkerber
        slab.center(vacuum=vacuum, axis=2)
288 1 tkerber
289 1 tkerber
    slab.adsorbate_info['cell'] = surface_cell
290 1 tkerber
    slab.adsorbate_info['sites'] = sites
291 1 tkerber
292 1 tkerber
    return slab
293 1 tkerber
294 1 tkerber