Statistiques
| Révision :

root / ase / atoms.py @ 17

Historique | Voir | Annoter | Télécharger (44,24 ko)

1 1 tkerber
# Copyright 2008, 2009 CAMd
2 1 tkerber
# (see accompanying license files for details).
3 1 tkerber
4 1 tkerber
"""Definition of the Atoms class.
5 1 tkerber

6 1 tkerber
This module defines the central object in the ASE package: the Atoms
7 1 tkerber
object.
8 1 tkerber
"""
9 1 tkerber
10 1 tkerber
from math import cos, sin
11 1 tkerber
12 1 tkerber
import numpy as np
13 1 tkerber
14 1 tkerber
from ase.atom import Atom
15 1 tkerber
from ase.data import atomic_numbers, chemical_symbols, atomic_masses
16 1 tkerber
import ase.units as units
17 1 tkerber
18 1 tkerber
class Atoms(object):
19 1 tkerber
    """Atoms object.
20 1 tkerber

21 1 tkerber
    The Atoms object can represent an isolated molecule, or a
22 1 tkerber
    periodically repeated structure.  It has a unit cell and
23 1 tkerber
    there may be periodic boundary conditions along any of the three
24 1 tkerber
    unit cell axes.
25 1 tkerber

26 1 tkerber
    Information about the atoms (atomic numbers and position) is
27 1 tkerber
    stored in ndarrays.  Optionally, there can be information about
28 1 tkerber
    tags, momenta, masses, magnetic moments and charges.
29 1 tkerber

30 1 tkerber
    In order to calculate energies, forces and stresses, a calculator
31 1 tkerber
    object has to attached to the atoms object.
32 1 tkerber

33 1 tkerber
    Parameters:
34 1 tkerber

35 1 tkerber
    symbols: str (formula) or list of str
36 1 tkerber
        Can be a string formula, a list of symbols or a list of
37 1 tkerber
        Atom objects.  Examples: 'H2O', 'COPt12', ['H', 'H', 'O'],
38 1 tkerber
        [Atom('Ne', (x, y, z)), ...].
39 1 tkerber
    positions: list of xyz-positions
40 1 tkerber
        Atomic positions.  Anything that can be converted to an
41 1 tkerber
        ndarray of shape (n, 3) will do: [(x1,y1,z1), (x2,y2,z2),
42 1 tkerber
        ...].
43 1 tkerber
    scaled_positions: list of scaled-positions
44 1 tkerber
        Like positions, but given in units of the unit cell.
45 1 tkerber
        Can not be set at the same time as positions.
46 1 tkerber
    numbers: list of int
47 1 tkerber
        Atomic numbers (use only one of symbols/numbers).
48 1 tkerber
    tags: list of int
49 1 tkerber
        Special purpose tags.
50 1 tkerber
    momenta: list of xyz-momenta
51 1 tkerber
        Momenta for all atoms.
52 1 tkerber
    masses: list of float
53 1 tkerber
        Atomic masses in atomic units.
54 1 tkerber
    magmoms: list of float
55 1 tkerber
        Magnetic moments.
56 1 tkerber
    charges: list of float
57 1 tkerber
        Atomic charges.
58 1 tkerber
    cell: 3x3 matrix
59 1 tkerber
        Unit cell vectors.  Can also be given as just three
60 1 tkerber
        numbers for orthorhombic cells.  Default value: [1, 1, 1].
61 1 tkerber
    pbc: one or three bool
62 1 tkerber
        Periodic boundary conditions flags.  Examples: True,
63 1 tkerber
        False, 0, 1, (1, 1, 0), (True, False, False).  Default
64 1 tkerber
        value: False.
65 1 tkerber
    constraint: constraint object(s)
66 1 tkerber
        Used for applying one or more constraints during structure
67 1 tkerber
        optimization.
68 1 tkerber
    calculator: calculator object
69 1 tkerber
        Used to attach a calculator for calulating energies and atomic
70 1 tkerber
        forces.
71 1 tkerber

72 1 tkerber
    Examples:
73 1 tkerber

74 1 tkerber
    These three are equivalent:
75 1 tkerber

76 1 tkerber
    >>> d = 1.104  # N2 bondlength
77 1 tkerber
    >>> a = Atoms('N2', [(0, 0, 0), (0, 0, d)])
78 1 tkerber
    >>> a = Atoms(numbers=[7, 7], positions=[(0, 0, 0), (0, 0, d)])
79 1 tkerber
    >>> a = Atoms([Atom('N', (0, 0, 0)), Atom('N', (0, 0, d)])
80 1 tkerber

81 1 tkerber
    FCC gold:
82 1 tkerber

83 1 tkerber
    >>> a = 4.05  # Gold lattice constant
84 1 tkerber
    >>> b = a / 2
85 1 tkerber
    >>> fcc = Atoms('Au',
86 1 tkerber
    ...             cell=[(0, b, b), (b, 0, b), (b, b, 0)],
87 1 tkerber
    ...             pbc=True)
88 1 tkerber

89 1 tkerber
    Hydrogen wire:
90 1 tkerber

91 1 tkerber
    >>> d = 0.9  # H-H distance
92 1 tkerber
    >>> L = 7.0
93 1 tkerber
    >>> h = Atoms('H', positions=[(0, L / 2, L / 2)],
94 1 tkerber
    ...           cell=(d, L, L),
95 1 tkerber
    ...           pbc=(1, 0, 0))
96 1 tkerber
    """
97 1 tkerber
98 1 tkerber
    def __init__(self, symbols=None,
99 1 tkerber
                 positions=None, numbers=None,
100 1 tkerber
                 tags=None, momenta=None, masses=None,
101 1 tkerber
                 magmoms=None, charges=None,
102 1 tkerber
                 scaled_positions=None,
103 1 tkerber
                 cell=None, pbc=None,
104 1 tkerber
                 constraint=None,
105 1 tkerber
                 calculator=None):
106 1 tkerber
107 1 tkerber
        atoms = None
108 1 tkerber
109 1 tkerber
        if hasattr(symbols, 'GetUnitCell'):
110 1 tkerber
            from ase.old import OldASEListOfAtomsWrapper
111 1 tkerber
            atoms = OldASEListOfAtomsWrapper(symbols)
112 1 tkerber
            symbols = None
113 1 tkerber
        elif hasattr(symbols, "get_positions"):
114 1 tkerber
            atoms = symbols
115 1 tkerber
            symbols = None
116 1 tkerber
        elif (isinstance(symbols, (list, tuple)) and
117 1 tkerber
              len(symbols) > 0 and isinstance(symbols[0], Atom)):
118 1 tkerber
            # Get data from a list or tuple of Atom objects:
119 1 tkerber
            data = zip(*[atom.get_data() for atom in symbols])
120 1 tkerber
            atoms = self.__class__(None, *data)
121 1 tkerber
            symbols = None
122 1 tkerber
123 1 tkerber
        if atoms is not None:
124 1 tkerber
            # Get data from another Atoms object:
125 1 tkerber
            if scaled_positions is not None:
126 1 tkerber
                raise NotImplementedError
127 1 tkerber
            if symbols is None and numbers is None:
128 1 tkerber
                numbers = atoms.get_atomic_numbers()
129 1 tkerber
            if positions is None:
130 1 tkerber
                positions = atoms.get_positions()
131 1 tkerber
            if tags is None and atoms.has('tags'):
132 1 tkerber
                tags = atoms.get_tags()
133 1 tkerber
            if momenta is None and atoms.has('momenta'):
134 1 tkerber
                momenta = atoms.get_momenta()
135 1 tkerber
            if magmoms is None and atoms.has('magmoms'):
136 1 tkerber
                magmoms = atoms.get_initial_magnetic_moments()
137 1 tkerber
            if masses is None and atoms.has('masses'):
138 1 tkerber
                masses = atoms.get_masses()
139 1 tkerber
            if charges is None and atoms.has('charges'):
140 1 tkerber
                charges = atoms.get_charges()
141 1 tkerber
            if cell is None:
142 1 tkerber
                cell = atoms.get_cell()
143 1 tkerber
            if pbc is None:
144 1 tkerber
                pbc = atoms.get_pbc()
145 1 tkerber
            if constraint is None:
146 1 tkerber
                constraint = [c.copy() for c in atoms.constraints]
147 1 tkerber
            if calculator is None:
148 1 tkerber
                calculator = atoms.get_calculator()
149 1 tkerber
150 1 tkerber
        self.arrays = {}
151 1 tkerber
152 1 tkerber
        if symbols is None:
153 1 tkerber
            if numbers is None:
154 1 tkerber
                if positions is not None:
155 1 tkerber
                    natoms = len(positions)
156 1 tkerber
                elif scaled_positions is not None:
157 1 tkerber
                    natoms = len(scaled_positions)
158 1 tkerber
                else:
159 1 tkerber
                    natoms = 0
160 1 tkerber
                numbers = np.zeros(natoms, int)
161 1 tkerber
            self.new_array('numbers', numbers, int)
162 1 tkerber
        else:
163 1 tkerber
            if numbers is not None:
164 1 tkerber
                raise ValueError(
165 1 tkerber
                    'Use only one of "symbols" and "numbers".')
166 1 tkerber
            else:
167 1 tkerber
                self.new_array('numbers', symbols2numbers(symbols), int)
168 1 tkerber
169 1 tkerber
        if cell is None:
170 1 tkerber
            cell = np.eye(3)
171 1 tkerber
        self.set_cell(cell)
172 1 tkerber
173 1 tkerber
        if positions is None:
174 1 tkerber
            if scaled_positions is None:
175 1 tkerber
                positions = np.zeros((len(self.arrays['numbers']), 3))
176 1 tkerber
            else:
177 1 tkerber
                positions = np.dot(scaled_positions, self._cell)
178 1 tkerber
        else:
179 1 tkerber
            if scaled_positions is not None:
180 1 tkerber
                raise RuntimeError, 'Both scaled and cartesian positions set!'
181 1 tkerber
        self.new_array('positions', positions, float, (3,))
182 1 tkerber
183 1 tkerber
        self.set_constraint(constraint)
184 1 tkerber
        self.set_tags(default(tags, 0))
185 1 tkerber
        self.set_momenta(default(momenta, (0.0, 0.0, 0.0)))
186 1 tkerber
        self.set_masses(default(masses, None))
187 1 tkerber
        self.set_initial_magnetic_moments(default(magmoms, 0.0))
188 1 tkerber
        self.set_charges(default(charges, 0.0))
189 1 tkerber
        if pbc is None:
190 1 tkerber
            pbc = False
191 1 tkerber
        self.set_pbc(pbc)
192 1 tkerber
193 1 tkerber
        self.adsorbate_info = {}
194 1 tkerber
195 1 tkerber
        self.set_calculator(calculator)
196 1 tkerber
197 1 tkerber
    def set_calculator(self, calc=None):
198 1 tkerber
        """Attach calculator object."""
199 1 tkerber
        if hasattr(calc, '_SetListOfAtoms'):
200 1 tkerber
            from ase.old import OldASECalculatorWrapper
201 1 tkerber
            calc = OldASECalculatorWrapper(calc, self)
202 1 tkerber
        if hasattr(calc, 'set_atoms'):
203 1 tkerber
            calc.set_atoms(self)
204 1 tkerber
        self.calc = calc
205 1 tkerber
206 1 tkerber
    def get_calculator(self):
207 1 tkerber
        """Get currently attached calculator object."""
208 1 tkerber
        return self.calc
209 1 tkerber
210 1 tkerber
    def set_constraint(self, constraint=None):
211 1 tkerber
        """Apply one or more constrains.
212 1 tkerber

213 1 tkerber
        The *constraint* argument must be one constraint object or a
214 1 tkerber
        list of constraint objects."""
215 1 tkerber
        if constraint is None:
216 1 tkerber
            self._constraints = []
217 1 tkerber
        else:
218 1 tkerber
            if isinstance(constraint, (list, tuple)):
219 1 tkerber
                self._constraints = constraint
220 1 tkerber
            else:
221 1 tkerber
                self._constraints = [constraint]
222 1 tkerber
223 1 tkerber
    def _get_constraints(self):
224 1 tkerber
        return self._constraints
225 1 tkerber
226 1 tkerber
    def _del_constraints(self):
227 1 tkerber
        self._constraints = []
228 1 tkerber
229 1 tkerber
    constraints = property(_get_constraints, set_constraint, _del_constraints,
230 1 tkerber
                           "Constraints of the atoms.")
231 1 tkerber
232 1 tkerber
    def set_cell(self, cell, scale_atoms=False, fix=None):
233 1 tkerber
        """Set unit cell vectors.
234 1 tkerber

235 1 tkerber
        Parameters:
236 1 tkerber

237 1 tkerber
        cell :
238 1 tkerber
            Unit cell.  A 3x3 matrix (the three unit cell vectors) or
239 1 tkerber
            just three numbers for an orthorhombic cell.
240 1 tkerber
        scale_atoms : bool
241 1 tkerber
            Fix atomic positions or move atoms with the unit cell?
242 1 tkerber
            Default behavior is to *not* move the atoms (scale_atoms=False).
243 1 tkerber

244 1 tkerber
        Examples:
245 1 tkerber

246 1 tkerber
        Two equivalent ways to define an orthorhombic cell:
247 1 tkerber

248 1 tkerber
        >>> a.set_cell([a, b, c])
249 1 tkerber
        >>> a.set_cell([(a, 0, 0), (0, b, 0), (0, 0, c)])
250 1 tkerber

251 1 tkerber
        FCC unit cell:
252 1 tkerber

253 1 tkerber
        >>> a.set_cell([(0, b, b), (b, 0, b), (b, b, 0)])
254 1 tkerber
        """
255 1 tkerber
256 1 tkerber
        if fix is not None:
257 1 tkerber
            raise TypeError('Please use scale_atoms=%s' % (not fix))
258 1 tkerber
259 1 tkerber
        cell = np.array(cell, float)
260 1 tkerber
        if cell.shape == (3,):
261 1 tkerber
            cell = np.diag(cell)
262 1 tkerber
        elif cell.shape != (3, 3):
263 1 tkerber
            raise ValueError('Cell must be length 3 sequence or '
264 1 tkerber
                             '3x3 matrix!')
265 1 tkerber
        if scale_atoms:
266 1 tkerber
            M = np.linalg.solve(self._cell, cell)
267 1 tkerber
            self.arrays['positions'][:] = np.dot(self.arrays['positions'], M)
268 1 tkerber
        self._cell = cell
269 1 tkerber
270 1 tkerber
    def get_cell(self):
271 1 tkerber
        """Get the three unit cell vectors as a 3x3 ndarray."""
272 1 tkerber
        return self._cell.copy()
273 1 tkerber
274 1 tkerber
    def set_pbc(self, pbc):
275 1 tkerber
        """Set periodic boundary condition flags."""
276 1 tkerber
        if isinstance(pbc, int):
277 1 tkerber
            pbc = (pbc,) * 3
278 1 tkerber
        self._pbc = np.array(pbc, bool)
279 1 tkerber
280 1 tkerber
    def get_pbc(self):
281 1 tkerber
        """Get periodic boundary condition flags."""
282 1 tkerber
        return self._pbc.copy()
283 1 tkerber
284 1 tkerber
    def new_array(self, name, a, dtype=None, shape=None):
285 1 tkerber
        """Add new array.
286 1 tkerber

287 1 tkerber
        If *shape* is not *None*, the shape of *a* will be checked."""
288 1 tkerber
        if dtype is not None:
289 1 tkerber
            a = np.array(a, dtype)
290 1 tkerber
        else:
291 1 tkerber
            a = a.copy()
292 1 tkerber
293 1 tkerber
        if name in self.arrays:
294 1 tkerber
            raise RuntimeError
295 1 tkerber
296 1 tkerber
        for b in self.arrays.values():
297 1 tkerber
            if len(a) != len(b):
298 1 tkerber
                raise ValueError('Array has wrong length: %d != %d.' %
299 1 tkerber
                                 (len(a), len(b)))
300 1 tkerber
            break
301 1 tkerber
302 1 tkerber
        if shape is not None and a.shape[1:] != shape:
303 1 tkerber
            raise ValueError('Array has wrong shape %s != %s.' %
304 1 tkerber
                             (a.shape, (a.shape[0:1] + shape)))
305 1 tkerber
306 1 tkerber
        self.arrays[name] = a
307 1 tkerber
308 1 tkerber
    def get_array(self, name, copy=True):
309 1 tkerber
        """Get an array.
310 1 tkerber

311 1 tkerber
        Returns a copy unless the optional argument copy is false.
312 1 tkerber
        """
313 2 tkerber
314 1 tkerber
        if copy:
315 1 tkerber
            return self.arrays[name].copy()
316 1 tkerber
        else:
317 1 tkerber
            return self.arrays[name]
318 1 tkerber
319 1 tkerber
    def set_array(self, name, a, dtype=None, shape=None):
320 1 tkerber
        """Update array.
321 1 tkerber

322 1 tkerber
        If *shape* is not *None*, the shape of *a* will be checked.
323 1 tkerber
        If *a* is *None*, then the array is deleted."""
324 1 tkerber
325 1 tkerber
        b = self.arrays.get(name)
326 1 tkerber
        if b is None:
327 1 tkerber
            if a is not None:
328 1 tkerber
                self.new_array(name, a, dtype, shape)
329 1 tkerber
        else:
330 1 tkerber
            if a is None:
331 1 tkerber
                del self.arrays[name]
332 1 tkerber
            else:
333 1 tkerber
                a = np.asarray(a)
334 1 tkerber
                if a.shape != b.shape:
335 1 tkerber
                    raise ValueError('Array has wrong shape %s != %s.' %
336 1 tkerber
                                     (a.shape, b.shape))
337 1 tkerber
                b[:] = a
338 1 tkerber
339 1 tkerber
    def has(self, name):
340 1 tkerber
        """Check for existance of array.
341 1 tkerber

342 1 tkerber
        name must be one of: 'tags', 'momenta', 'masses', 'magmoms',
343 1 tkerber
        'charges'."""
344 1 tkerber
        return name in self.arrays
345 1 tkerber
346 1 tkerber
    def set_atomic_numbers(self, numbers):
347 1 tkerber
        """Set atomic numbers."""
348 1 tkerber
        self.set_array('numbers', numbers, int, ())
349 1 tkerber
350 1 tkerber
    def get_atomic_numbers(self):
351 1 tkerber
        """Get integer array of atomic numbers."""
352 1 tkerber
        return self.arrays['numbers']
353 1 tkerber
354 1 tkerber
    def set_chemical_symbols(self, symbols):
355 1 tkerber
        """Set chemical symbols."""
356 1 tkerber
        self.set_array('numbers', symbols2numbers(symbols), int, ())
357 1 tkerber
358 1 tkerber
    def get_chemical_symbols(self, reduce=False):
359 1 tkerber
        """Get list of chemical symbol strings.
360 1 tkerber

361 1 tkerber
        If reduce is True, a single string is returned, where repeated
362 1 tkerber
        elements have been contracted to a single symbol and a number.
363 1 tkerber
        E.g. instead of ['C', 'O', 'O', 'H'], the string 'CO2H' is returned.
364 1 tkerber
        """
365 1 tkerber
        if not reduce:
366 1 tkerber
            return [chemical_symbols[Z] for Z in self.arrays['numbers']]
367 1 tkerber
        else:
368 1 tkerber
            num = self.get_atomic_numbers()
369 1 tkerber
            N = len(num)
370 1 tkerber
            dis = np.concatenate(([0], np.arange(1, N)[num[1:] != num[:-1]]))
371 1 tkerber
            repeat = np.append(dis[1:], N) - dis
372 1 tkerber
            symbols = ''.join([chemical_symbols[num[d]] + str(r) * (r != 1)
373 1 tkerber
                               for r, d in zip(repeat, dis)])
374 1 tkerber
            return symbols
375 1 tkerber
376 1 tkerber
    def set_tags(self, tags):
377 1 tkerber
        """Set tags for all atoms."""
378 1 tkerber
        self.set_array('tags', tags, int, ())
379 1 tkerber
380 1 tkerber
    def get_tags(self):
381 1 tkerber
        """Get integer array of tags."""
382 1 tkerber
        if 'tags' in self.arrays:
383 1 tkerber
            return self.arrays['tags'].copy()
384 1 tkerber
        else:
385 1 tkerber
            return np.zeros(len(self), int)
386 1 tkerber
387 1 tkerber
    def set_momenta(self, momenta):
388 1 tkerber
        """Set momenta."""
389 1 tkerber
        if len(self.constraints) > 0 and momenta is not None:
390 1 tkerber
            momenta = np.array(momenta)  # modify a copy
391 1 tkerber
            for constraint in self.constraints:
392 1 tkerber
                constraint.adjust_forces(self.arrays['positions'], momenta)
393 1 tkerber
        self.set_array('momenta', momenta, float, (3,))
394 1 tkerber
395 1 tkerber
    def set_velocities(self, velocities):
396 1 tkerber
        """Set the momenta by specifying the velocities."""
397 1 tkerber
        self.set_momenta(self.get_masses()[:,np.newaxis] * velocities)
398 1 tkerber
399 1 tkerber
    def get_momenta(self):
400 1 tkerber
        """Get array of momenta."""
401 1 tkerber
        if 'momenta' in self.arrays:
402 1 tkerber
            return self.arrays['momenta'].copy()
403 1 tkerber
        else:
404 1 tkerber
            return np.zeros((len(self), 3))
405 1 tkerber
406 1 tkerber
    def set_masses(self, masses='defaults'):
407 1 tkerber
        """Set atomic masses.
408 1 tkerber

409 1 tkerber
        The array masses should contain the a list masses.  In case
410 1 tkerber
        the masses argument is not given or for those elements of the
411 1 tkerber
        masses list that are None, standard values are set."""
412 1 tkerber
413 1 tkerber
        if masses == 'defaults':
414 1 tkerber
            masses = atomic_masses[self.arrays['numbers']]
415 1 tkerber
        elif isinstance(masses, (list, tuple)):
416 1 tkerber
            newmasses = []
417 1 tkerber
            for m, Z in zip(masses, self.arrays['numbers']):
418 1 tkerber
                if m is None:
419 1 tkerber
                    newmasses.append(atomic_masses[Z])
420 1 tkerber
                else:
421 1 tkerber
                    newmasses.append(m)
422 1 tkerber
            masses = newmasses
423 1 tkerber
        self.set_array('masses', masses, float, ())
424 1 tkerber
425 1 tkerber
    def get_masses(self):
426 1 tkerber
        """Get array of masses."""
427 1 tkerber
        if 'masses' in self.arrays:
428 1 tkerber
            return self.arrays['masses'].copy()
429 1 tkerber
        else:
430 1 tkerber
            return atomic_masses[self.arrays['numbers']]
431 1 tkerber
432 1 tkerber
    def set_initial_magnetic_moments(self, magmoms):
433 1 tkerber
        """Set the initial magnetic moments."""
434 1 tkerber
        self.set_array('magmoms', magmoms, float, ())
435 1 tkerber
436 1 tkerber
    def set_magnetic_moments(self, magmoms):
437 1 tkerber
        print 'Please use set_initial_magnetic_moments() instead!'
438 1 tkerber
        self.set_initial_magnetic_moments(magmoms)
439 1 tkerber
440 1 tkerber
    def get_initial_magnetic_moments(self):
441 1 tkerber
        """Get array of initial magnetic moments."""
442 1 tkerber
        if 'magmoms' in self.arrays:
443 1 tkerber
            return self.arrays['magmoms'].copy()
444 1 tkerber
        else:
445 1 tkerber
            return np.zeros(len(self))
446 1 tkerber
447 1 tkerber
    def get_magnetic_moments(self):
448 1 tkerber
        """Get calculated local magnetic moments."""
449 1 tkerber
        if self.calc is None:
450 1 tkerber
            raise RuntimeError('Atoms object has no calculator.')
451 1 tkerber
        if self.calc.get_spin_polarized():
452 1 tkerber
            return self.calc.get_magnetic_moments(self)
453 1 tkerber
        else:
454 1 tkerber
            return np.zeros(len(self))
455 1 tkerber
456 1 tkerber
    def get_magnetic_moment(self):
457 1 tkerber
        """Get calculated total magnetic moment."""
458 1 tkerber
        if self.calc is None:
459 1 tkerber
            raise RuntimeError('Atoms object has no calculator.')
460 1 tkerber
        if self.calc.get_spin_polarized():
461 1 tkerber
            return self.calc.get_magnetic_moment(self)
462 1 tkerber
        else:
463 1 tkerber
            return 0.0
464 1 tkerber
465 1 tkerber
    def set_charges(self, charges):
466 1 tkerber
        """Set charges."""
467 1 tkerber
        self.set_array('charges', charges, float, ())
468 1 tkerber
469 1 tkerber
    def get_charges(self):
470 1 tkerber
        """Get array of charges."""
471 1 tkerber
        if 'charges' in self.arrays:
472 1 tkerber
            return self.arrays['charges'].copy()
473 1 tkerber
        else:
474 1 tkerber
            return np.zeros(len(self))
475 1 tkerber
476 1 tkerber
    def set_positions(self, newpositions):
477 1 tkerber
        """Set positions."""
478 1 tkerber
        positions = self.arrays['positions']
479 1 tkerber
        if self.constraints:
480 1 tkerber
            newpositions = np.asarray(newpositions, float)
481 1 tkerber
            for constraint in self.constraints:
482 1 tkerber
                constraint.adjust_positions(positions, newpositions)
483 1 tkerber
484 1 tkerber
        self.set_array('positions', newpositions, shape=(3,))
485 1 tkerber
486 1 tkerber
    def get_positions(self):
487 1 tkerber
        """Get array of positions."""
488 1 tkerber
        return self.arrays['positions'].copy()
489 1 tkerber
490 1 tkerber
    def get_potential_energy(self):
491 1 tkerber
        """Calculate potential energy."""
492 1 tkerber
        if self.calc is None:
493 1 tkerber
            raise RuntimeError('Atoms object has no calculator.')
494 1 tkerber
        return self.calc.get_potential_energy(self)
495 1 tkerber
496 1 tkerber
    def get_potential_energies(self):
497 1 tkerber
        """Calculate the potential energies of all the atoms.
498 1 tkerber

499 1 tkerber
        Only available with calculators supporting per-atom energies
500 1 tkerber
        (e.g. classical potentials).
501 1 tkerber
        """
502 1 tkerber
        if self.calc is None:
503 1 tkerber
            raise RuntimeError('Atoms object has no calculator.')
504 1 tkerber
        return self.calc.get_potential_energies(self)
505 1 tkerber
506 1 tkerber
    def get_kinetic_energy(self):
507 1 tkerber
        """Get the kinetic energy."""
508 1 tkerber
        momenta = self.arrays.get('momenta')
509 1 tkerber
        if momenta is None:
510 1 tkerber
            return 0.0
511 1 tkerber
        return 0.5 * np.vdot(momenta, self.get_velocities())
512 1 tkerber
513 1 tkerber
    def get_velocities(self):
514 1 tkerber
        """Get array of velocities."""
515 1 tkerber
        momenta = self.arrays.get('momenta')
516 1 tkerber
        if momenta is None:
517 1 tkerber
            return None
518 1 tkerber
        m = self.arrays.get('masses')
519 1 tkerber
        if m is None:
520 1 tkerber
            m = atomic_masses[self.arrays['numbers']]
521 1 tkerber
        return momenta / m.reshape(-1, 1)
522 1 tkerber
523 1 tkerber
    def get_total_energy(self):
524 1 tkerber
        """Get the total energy - potential plus kinetic energy."""
525 1 tkerber
        return self.get_potential_energy() + self.get_kinetic_energy()
526 1 tkerber
527 1 tkerber
    def get_forces(self, apply_constraint=True):
528 1 tkerber
        """Calculate atomic forces.
529 1 tkerber

530 1 tkerber
        Ask the attached calculator to calculate the forces and apply
531 1 tkerber
        constraints.  Use *apply_constraint=False* to get the raw
532 1 tkerber
        forces."""
533 1 tkerber
534 1 tkerber
        if self.calc is None:
535 1 tkerber
            raise RuntimeError('Atoms object has no calculator.')
536 1 tkerber
        forces = self.calc.get_forces(self)
537 1 tkerber
        if apply_constraint:
538 1 tkerber
            for constraint in self.constraints:
539 1 tkerber
                constraint.adjust_forces(self.arrays['positions'], forces)
540 1 tkerber
        return forces
541 1 tkerber
542 1 tkerber
    def get_stress(self):
543 1 tkerber
        """Calculate stress tensor.
544 1 tkerber

545 1 tkerber
        Returns an array of the six independent components of the
546 1 tkerber
        symmetric stress tensor, in the traditional order
547 1 tkerber
        (s_xx, s_yy, s_zz, s_yz, s_xz, s_xy).
548 1 tkerber
        """
549 1 tkerber
        if self.calc is None:
550 1 tkerber
            raise RuntimeError('Atoms object has no calculator.')
551 1 tkerber
        stress = self.calc.get_stress(self)
552 1 tkerber
        shape = getattr(stress, "shape", None)
553 1 tkerber
        if shape == (3,3):
554 1 tkerber
            return np.array([stress[0,0], stress[1,1], stress[2,2],
555 1 tkerber
                             stress[1,2], stress[0,2], stress[0,1]])
556 1 tkerber
        else:
557 1 tkerber
            # Hopefully a 6-vector, but don't check in case some weird
558 1 tkerber
            # calculator does something else.
559 1 tkerber
            return stress
560 1 tkerber
561 1 tkerber
    def get_stresses(self):
562 1 tkerber
        """Calculate the stress-tensor of all the atoms.
563 1 tkerber

564 1 tkerber
        Only available with calculators supporting per-atom energies and
565 1 tkerber
        stresses (e.g. classical potentials).  Even for such calculators
566 1 tkerber
        there is a certain arbitrariness in defining per-atom stresses.
567 1 tkerber
        """
568 1 tkerber
        if self.calc is None:
569 1 tkerber
            raise RuntimeError('Atoms object has no calculator.')
570 1 tkerber
        return self.calc.get_stresses(self)
571 1 tkerber
572 1 tkerber
    def get_dipole_moment(self):
573 1 tkerber
        """Calculate the electric dipole moment for the atoms object.
574 1 tkerber

575 1 tkerber
        Only available for calculators which has a get_dipole_moment() method."""
576 1 tkerber
        if self.calc is None:
577 1 tkerber
            raise RuntimeError('Atoms object has no calculator.')
578 1 tkerber
        try:
579 1 tkerber
            dipole = self.calc.get_dipole_moment(self)
580 1 tkerber
        except AttributeError:
581 1 tkerber
            raise AttributeError('Calculator object has no get_dipole_moment method.')
582 1 tkerber
        return dipole
583 1 tkerber
584 1 tkerber
    def copy(self):
585 1 tkerber
        """Return a copy."""
586 1 tkerber
        import copy
587 3 tkerber
        atoms = Atoms(cell=self._cell, pbc=self._pbc)
588 1 tkerber
589 1 tkerber
        atoms.arrays = {}
590 1 tkerber
        for name, a in self.arrays.items():
591 1 tkerber
            atoms.arrays[name] = a.copy()
592 1 tkerber
        atoms.constraints = copy.deepcopy(self.constraints)
593 1 tkerber
        atoms.adsorbate_info = copy.deepcopy(self.adsorbate_info)
594 1 tkerber
        return atoms
595 1 tkerber
596 1 tkerber
    def __len__(self):
597 1 tkerber
        return len(self.arrays['positions'])
598 1 tkerber
599 1 tkerber
    def get_number_of_atoms(self):
600 1 tkerber
        """Returns the number of atoms.
601 1 tkerber

602 1 tkerber
        Equivalent to len(atoms) in the standard ASE Atoms class.
603 1 tkerber
        """
604 1 tkerber
        return len(self)
605 1 tkerber
606 1 tkerber
    def __repr__(self):
607 1 tkerber
        num = self.get_atomic_numbers()
608 1 tkerber
        N = len(num)
609 1 tkerber
        if N == 0:
610 1 tkerber
            symbols = ''
611 1 tkerber
        elif N <= 60:
612 1 tkerber
            symbols = self.get_chemical_symbols(reduce=True)
613 1 tkerber
        else:
614 1 tkerber
            symbols = ''.join([chemical_symbols[Z] for Z in num[:15]]) + '...'
615 1 tkerber
        s = "%s(symbols='%s', " %(self.__class__.__name__, symbols)
616 1 tkerber
        for name in self.arrays:
617 1 tkerber
            if name == 'numbers':
618 1 tkerber
                continue
619 1 tkerber
            s += '%s=..., ' % name
620 1 tkerber
        if (self._cell - np.diag(self._cell.diagonal())).any():
621 1 tkerber
            s += 'cell=%s, ' % self._cell.tolist()
622 1 tkerber
        else:
623 1 tkerber
            s += 'cell=%s, ' % self._cell.diagonal().tolist()
624 1 tkerber
        s += 'pbc=%s, ' % self._pbc.tolist()
625 1 tkerber
        if len(self.constraints) == 1:
626 1 tkerber
            s += 'constraint=%s, ' % repr(self.constraints[0])
627 1 tkerber
        if len(self.constraints) > 1:
628 1 tkerber
            s += 'constraint=%s, ' % repr(self.constraints)
629 1 tkerber
        if self.calc is not None:
630 1 tkerber
            s += 'calculator=%s(...), ' % self.calc.__class__.__name__
631 1 tkerber
        return s[:-2] + ')'
632 1 tkerber
633 1 tkerber
    def __add__(self, other):
634 1 tkerber
        atoms = self.copy()
635 1 tkerber
        atoms += other
636 1 tkerber
        return atoms
637 1 tkerber
638 1 tkerber
    def extend(self, other):
639 1 tkerber
        """Extend atoms object by appending atoms from *other*."""
640 1 tkerber
        if isinstance(other, Atom):
641 1 tkerber
            other = self.__class__([other])
642 1 tkerber
643 1 tkerber
        n1 = len(self)
644 1 tkerber
        n2 = len(other)
645 1 tkerber
646 1 tkerber
        for name, a1 in self.arrays.items():
647 1 tkerber
            a = np.zeros((n1 + n2,) + a1.shape[1:], a1.dtype)
648 1 tkerber
            a[:n1] = a1
649 1 tkerber
            a2 = other.arrays.get(name)
650 1 tkerber
            if a2 is not None:
651 1 tkerber
                a[n1:] = a2
652 1 tkerber
            self.arrays[name] = a
653 1 tkerber
654 1 tkerber
        for name, a2 in other.arrays.items():
655 1 tkerber
            if name in self.arrays:
656 1 tkerber
                continue
657 1 tkerber
            a = np.zeros((n1 + n2,) + a2.shape[1:], a2.dtype)
658 1 tkerber
            a[n1:] = a2
659 1 tkerber
            self.set_array(name, a)
660 1 tkerber
661 1 tkerber
        return self
662 1 tkerber
663 1 tkerber
    __iadd__ = extend
664 1 tkerber
665 1 tkerber
    def append(self, atom):
666 1 tkerber
        """Append atom to end."""
667 1 tkerber
        self.extend(self.__class__([atom]))
668 1 tkerber
669 1 tkerber
    def __getitem__(self, i):
670 1 tkerber
        """Return a subset of the atoms.
671 1 tkerber

672 1 tkerber
        i -- scalar integer, list of integers, or slice object
673 1 tkerber
        describing which atoms to return.
674 1 tkerber

675 1 tkerber
        If i is a scalar, return an Atom object. If i is a list or a
676 1 tkerber
        slice, return an Atoms object with the same cell, pbc, and
677 1 tkerber
        other associated info as the original Atoms object. The
678 1 tkerber
        indices of the constraints will be shuffled so that they match
679 1 tkerber
        the indexing in the subset returned.
680 1 tkerber

681 1 tkerber
        """
682 1 tkerber
        if isinstance(i, int):
683 1 tkerber
            natoms = len(self)
684 1 tkerber
            if i < -natoms or i >= natoms:
685 1 tkerber
                raise IndexError('Index out of range.')
686 1 tkerber
687 1 tkerber
            return Atom(atoms=self, index=i)
688 1 tkerber
689 1 tkerber
        import copy
690 1 tkerber
        from ase.constraints import FixConstraint
691 1 tkerber
692 1 tkerber
        atoms = self.__class__(cell=self._cell, pbc=self._pbc)
693 1 tkerber
        # TODO: Do we need to shuffle indices in adsorbate_info too?
694 1 tkerber
        atoms.adsorbate_info = self.adsorbate_info
695 1 tkerber
696 1 tkerber
        atoms.arrays = {}
697 1 tkerber
        for name, a in self.arrays.items():
698 1 tkerber
            atoms.arrays[name] = a[i].copy()
699 1 tkerber
700 1 tkerber
        # Constraints need to be deepcopied, since we need to shuffle
701 1 tkerber
        # the indices
702 1 tkerber
        atoms.constraints = copy.deepcopy(self.constraints)
703 1 tkerber
        condel = []
704 1 tkerber
        for con in atoms.constraints:
705 1 tkerber
            if isinstance(con, FixConstraint):
706 1 tkerber
                try:
707 1 tkerber
                    con.index_shuffle(i)
708 1 tkerber
                except IndexError:
709 1 tkerber
                    condel.append(con)
710 1 tkerber
        for con in condel:
711 1 tkerber
            atoms.constraints.remove(con)
712 1 tkerber
        return atoms
713 1 tkerber
714 1 tkerber
    def __delitem__(self, i):
715 1 tkerber
        if len(self._constraints) > 0:
716 1 tkerber
            raise RuntimeError('Remove constraint using set_constraint() ' +
717 1 tkerber
                               'before deleting atoms.')
718 1 tkerber
        mask = np.ones(len(self), bool)
719 1 tkerber
        mask[i] = False
720 1 tkerber
        for name, a in self.arrays.items():
721 1 tkerber
            self.arrays[name] = a[mask]
722 1 tkerber
723 1 tkerber
    def pop(self, i=-1):
724 1 tkerber
        """Remove and return atom at index *i* (default last)."""
725 1 tkerber
        atom = self[i]
726 1 tkerber
        atom.cut_reference_to_atoms()
727 1 tkerber
        del self[i]
728 1 tkerber
        return atom
729 1 tkerber
730 1 tkerber
    def __imul__(self, m):
731 1 tkerber
        """In-place repeat of atoms."""
732 1 tkerber
        if isinstance(m, int):
733 1 tkerber
            m = (m, m, m)
734 1 tkerber
735 1 tkerber
        M = np.product(m)
736 1 tkerber
        n = len(self)
737 1 tkerber
738 1 tkerber
        for name, a in self.arrays.items():
739 1 tkerber
            self.arrays[name] = np.tile(a, (M,) + (1,) * (len(a.shape) - 1))
740 1 tkerber
741 1 tkerber
        positions = self.arrays['positions']
742 1 tkerber
        i0 = 0
743 1 tkerber
        for m2 in range(m[2]):
744 1 tkerber
            for m1 in range(m[1]):
745 1 tkerber
                for m0 in range(m[0]):
746 1 tkerber
                    i1 = i0 + n
747 1 tkerber
                    positions[i0:i1] += np.dot((m0, m1, m2), self._cell)
748 1 tkerber
                    i0 = i1
749 1 tkerber
750 1 tkerber
        if self.constraints is not None:
751 1 tkerber
            self.constraints = [c.repeat(m, n) for c in self.constraints]
752 1 tkerber
753 1 tkerber
        self._cell = np.array([m[c] * self._cell[c] for c in range(3)])
754 1 tkerber
755 1 tkerber
        return self
756 1 tkerber
757 1 tkerber
    def repeat(self, rep):
758 1 tkerber
        """Create new repeated atoms object.
759 1 tkerber

760 1 tkerber
        The *rep* argument should be a sequence of three positive
761 1 tkerber
        integers like *(2,3,1)* or a single integer (*r*) equivalent
762 1 tkerber
        to *(r,r,r)*."""
763 1 tkerber
764 1 tkerber
        atoms = self.copy()
765 1 tkerber
        atoms *= rep
766 1 tkerber
        return atoms
767 1 tkerber
768 1 tkerber
    __mul__ = repeat
769 1 tkerber
770 1 tkerber
    def translate(self, displacement):
771 1 tkerber
        """Translate atomic positions.
772 1 tkerber

773 1 tkerber
        The displacement argument can be a float an xyz vector or an
774 1 tkerber
        nx3 array (where n is the number of atoms)."""
775 1 tkerber
776 1 tkerber
        self.arrays['positions'] += np.array(displacement)
777 1 tkerber
778 1 tkerber
    def center(self, vacuum=None, axis=None):
779 1 tkerber
        """Center atoms in unit cell.
780 1 tkerber

781 1 tkerber
        Centers the atoms in the unit cell, so there is the same
782 1 tkerber
        amount of vacuum on all sides.
783 1 tkerber

784 1 tkerber
        Parameters:
785 1 tkerber

786 1 tkerber
        vacuum (default: None): If specified adjust the amount of
787 1 tkerber
        vacuum when centering.  If vacuum=10.0 there will thus be 10
788 1 tkerber
        Angstrom of vacuum on each side.
789 1 tkerber

790 1 tkerber
        axis (default: None): If specified, only act on the specified
791 1 tkerber
        axis.  Default: Act on all axes.
792 1 tkerber
        """
793 1 tkerber
        # Find the orientations of the faces of the unit cell
794 1 tkerber
        c = self.get_cell()
795 1 tkerber
        dirs = np.zeros_like(c)
796 1 tkerber
        for i in range(3):
797 1 tkerber
            dirs[i] = np.cross(c[i-1], c[i-2])
798 1 tkerber
            dirs[i] /= np.sqrt(np.dot(dirs[i], dirs[i])) #Normalize
799 1 tkerber
            if np.dot(dirs[i], c[i]) < 0.0:
800 1 tkerber
                dirs[i] *= -1
801 1 tkerber
802 1 tkerber
        # Now, decide how much each basis vector should be made longer
803 1 tkerber
        if axis is None:
804 1 tkerber
            axes = (0,1,2)
805 1 tkerber
        else:
806 1 tkerber
            axes = (axis,)
807 1 tkerber
        p = self.arrays['positions']
808 1 tkerber
        longer = np.zeros(3)
809 1 tkerber
        shift = np.zeros(3)
810 1 tkerber
        for i in axes:
811 1 tkerber
            p0 = np.dot(p, dirs[i]).min()
812 1 tkerber
            p1 = np.dot(p, dirs[i]).max()
813 1 tkerber
            height = np.dot(c[i], dirs[i])
814 1 tkerber
            if vacuum is not None:
815 1 tkerber
                lng = (p1 - p0 + 2*vacuum) - height
816 1 tkerber
            else:
817 1 tkerber
                lng = 0.0  # Do not change unit cell size!
818 1 tkerber
            top = lng + height - p1
819 1 tkerber
            shf = 0.5 * (top - p0)
820 1 tkerber
            cosphi = np.dot(c[i], dirs[i]) / np.sqrt(np.dot(c[i], c[i]))
821 1 tkerber
            longer[i] = lng / cosphi
822 1 tkerber
            shift[i] = shf / cosphi
823 1 tkerber
824 1 tkerber
        # Now, do it!
825 1 tkerber
        translation = np.zeros(3)
826 1 tkerber
        for i in axes:
827 1 tkerber
            nowlen = np.sqrt(np.dot(c[i], c[i]))
828 1 tkerber
            self._cell[i] *= 1 + longer[i] / nowlen
829 1 tkerber
            translation += shift[i] * c[i] / nowlen
830 1 tkerber
        self.arrays['positions'] += translation
831 1 tkerber
832 1 tkerber
    def get_center_of_mass(self, scaled = False):
833 1 tkerber
        """Get the center of mass.
834 1 tkerber

835 1 tkerber
        If scaled=True the center of mass in scaled coordinates
836 1 tkerber
        is returned."""
837 1 tkerber
        m = self.arrays.get('masses')
838 1 tkerber
        if m is None:
839 1 tkerber
            m = atomic_masses[self.arrays['numbers']]
840 1 tkerber
        com = np.dot(m, self.arrays['positions']) / m.sum()
841 1 tkerber
        if scaled:
842 1 tkerber
            return np.dot(np.linalg.inv(self._cell), com)
843 1 tkerber
        else:
844 1 tkerber
            return com
845 1 tkerber
846 1 tkerber
    def get_moments_of_inertia(self):
847 1 tkerber
        '''Get the moments of inertia
848 1 tkerber

849 1 tkerber
        The three principal moments of inertia are computed from the
850 1 tkerber
        eigenvalues of the inertial tensor. periodic boundary
851 1 tkerber
        conditions are ignored. Units of the moments of inertia are
852 1 tkerber
        amu*angstrom**2.
853 1 tkerber
        '''
854 1 tkerber
855 1 tkerber
        com = self.get_center_of_mass()
856 1 tkerber
        positions = self.get_positions()
857 1 tkerber
        positions -= com #translate center of mass to origin
858 1 tkerber
        masses = self.get_masses()
859 1 tkerber
860 1 tkerber
        #initialize elements of the inertial tensor
861 1 tkerber
        I11 = I22 = I33 = I12 = I13 = I23 = 0.0
862 1 tkerber
        for i in range(len(self)):
863 1 tkerber
            x,y,z = positions[i]
864 1 tkerber
            m = masses[i]
865 1 tkerber
866 1 tkerber
            I11 += m*(y**2 + z**2)
867 1 tkerber
            I22 += m*(x**2 + z**2)
868 1 tkerber
            I33 += m*(x**2 + y**2)
869 1 tkerber
            I12 += -m*x*y
870 1 tkerber
            I13 += -m*x*z
871 1 tkerber
            I23 += -m*y*z
872 1 tkerber
873 1 tkerber
        I = np.array([[I11, I12, I13],
874 1 tkerber
                      [I12, I22, I23],
875 1 tkerber
                      [I13, I23, I33]])
876 1 tkerber
877 1 tkerber
        evals, evecs = np.linalg.eig(I)
878 1 tkerber
        return evals
879 1 tkerber
880 1 tkerber
    def rotate(self, v, a=None, center=(0, 0, 0), rotate_cell=False):
881 1 tkerber
        """Rotate atoms.
882 1 tkerber

883 1 tkerber
        Rotate the angle *a* around the vector *v*.  If *a* is not
884 1 tkerber
        given, the length of *v* is used as the angle.  If *a* is a
885 1 tkerber
        vector, then *v* is rotated into *a*.  The point at *center*
886 1 tkerber
        is fixed.  Use *center='COM'* to fix the center of mass.
887 1 tkerber
        Vectors can also be strings: 'x', '-x', 'y', ... .
888 1 tkerber
        If both *v* and *a* are vectors: rotate *v* into *a*.
889 1 tkerber

890 1 tkerber
        Examples:
891 1 tkerber

892 1 tkerber
        Rotate 90 degrees around the z-axis, so that the x-axis is
893 1 tkerber
        rotated into the y-axis:
894 1 tkerber

895 1 tkerber
        >>> a = pi / 2
896 1 tkerber
        >>> atoms.rotate('z', a)
897 1 tkerber
        >>> atoms.rotate((0, 0, 1), a)
898 1 tkerber
        >>> atoms.rotate('-z', -a)
899 1 tkerber
        >>> atoms.rotate((0, 0, a))
900 1 tkerber
        >>> atoms.rotate('x', 'y')
901 1 tkerber
        """
902 1 tkerber
903 1 tkerber
        norm = np.linalg.norm
904 1 tkerber
        v = string2vector(v)
905 1 tkerber
        if a is None:
906 1 tkerber
            a = norm(v)
907 1 tkerber
        if isinstance(a, (float, int)):
908 1 tkerber
            v /= norm(v)
909 1 tkerber
            c = cos(a)
910 1 tkerber
            s = sin(a)
911 1 tkerber
        else:
912 1 tkerber
            v2 = string2vector(a)
913 1 tkerber
            v /= norm(v)
914 1 tkerber
            v2 /= norm(v2)
915 1 tkerber
            c = np.dot(v, v2)
916 1 tkerber
            v = np.cross(v, v2)
917 1 tkerber
            s = norm(v)
918 1 tkerber
            # In case *v* and *a* are parallel, np.cross(v, v2) vanish
919 1 tkerber
            # and can't be used as a rotation axis. However, in this
920 1 tkerber
            # case any rotation axis perpendicular to v2 will do.
921 1 tkerber
            eps = 1e-7
922 1 tkerber
            if s < eps:
923 1 tkerber
                v = np.cross((0, 0, 1), v2)
924 1 tkerber
            if norm(v) < eps:
925 1 tkerber
                v = np.cross((1, 0, 0), v2)
926 1 tkerber
            assert norm(v) >= eps
927 1 tkerber
            if s > 0: v /= s
928 1 tkerber
929 1 tkerber
        if isinstance(center, str) and center.lower() == 'com':
930 1 tkerber
            center = self.get_center_of_mass()
931 1 tkerber
932 1 tkerber
        p = self.arrays['positions'] - center
933 1 tkerber
        self.arrays['positions'][:] = (c * p -
934 1 tkerber
                                       np.cross(p, s * v) +
935 1 tkerber
                                       np.outer(np.dot(p, v), (1.0 - c) * v)+
936 1 tkerber
                                       center)
937 1 tkerber
        if rotate_cell:
938 1 tkerber
            rotcell = self.get_cell()
939 1 tkerber
            rotcell[:] = (c * rotcell -
940 1 tkerber
                          np.cross(rotcell, s * v) +
941 1 tkerber
                          np.outer(np.dot(rotcell, v), (1.0 - c) * v))
942 1 tkerber
            self.set_cell(rotcell)
943 1 tkerber
944 1 tkerber
    def rotate_euler(self, center=(0, 0, 0), phi=0.0, theta=0.0, psi=0.0):
945 1 tkerber
        """Rotate atoms via Euler angles.
946 1 tkerber

947 1 tkerber
        See e.g http://mathworld.wolfram.com/EulerAngles.html for explanation.
948 1 tkerber

949 1 tkerber
        Parameters:
950 1 tkerber

951 1 tkerber
        center :
952 1 tkerber
            The point to rotate about. A sequence of length 3 with the
953 1 tkerber
            coordinates, or 'COM' to select the center of mass.
954 1 tkerber
        phi :
955 1 tkerber
            The 1st rotation angle around the z axis.
956 1 tkerber
        theta :
957 1 tkerber
            Rotation around the x axis.
958 1 tkerber
        psi :
959 1 tkerber
            2nd rotation around the z axis.
960 1 tkerber

961 1 tkerber
        """
962 1 tkerber
        if isinstance(center, str) and center.lower() == 'com':
963 1 tkerber
            center = self.get_center_of_mass()
964 1 tkerber
        else:
965 1 tkerber
            center = np.array(center)
966 1 tkerber
        # First move the molecule to the origin In contrast to MATLAB,
967 1 tkerber
        # numpy broadcasts the smaller array to the larger row-wise,
968 1 tkerber
        # so there is no need to play with the Kronecker product.
969 1 tkerber
        rcoords = self.positions - center
970 1 tkerber
        # First Euler rotation about z in matrix form
971 1 tkerber
        D = np.array(((cos(phi), sin(phi), 0.),
972 1 tkerber
                      (-sin(phi), cos(phi), 0.),
973 1 tkerber
                      (0., 0., 1.)))
974 1 tkerber
        # Second Euler rotation about x:
975 1 tkerber
        C = np.array(((1., 0., 0.),
976 1 tkerber
                      (0., cos(theta), sin(theta)),
977 1 tkerber
                      (0., -sin(theta), cos(theta))))
978 1 tkerber
        # Third Euler rotation, 2nd rotation about z:
979 1 tkerber
        B = np.array(((cos(psi), sin(psi), 0.),
980 1 tkerber
                      (-sin(psi), cos(psi), 0.),
981 1 tkerber
                      (0., 0., 1.)))
982 1 tkerber
        # Total Euler rotation
983 1 tkerber
        A = np.dot(B, np.dot(C, D))
984 1 tkerber
        # Do the rotation
985 1 tkerber
        rcoords = np.dot(A, np.transpose(rcoords))
986 1 tkerber
        # Move back to the rotation point
987 1 tkerber
        self.positions = np.transpose(rcoords) + center
988 1 tkerber
989 1 tkerber
    def get_dihedral(self,list):
990 1 tkerber
        """
991 1 tkerber
        calculate dihedral angle between the vectors list[0]->list[1] and list[2]->list[3],
992 1 tkerber
        where list contains the atomic indexes in question.
993 1 tkerber
        """
994 1 tkerber
        # vector 0->1, 1->2, 2->3 and their normalized cross products:
995 1 tkerber
        a    = self.positions[list[1]]-self.positions[list[0]]
996 1 tkerber
        b    = self.positions[list[2]]-self.positions[list[1]]
997 1 tkerber
        c    = self.positions[list[3]]-self.positions[list[2]]
998 1 tkerber
        bxa  = np.cross(b,a)
999 1 tkerber
        bxa /= np.sqrt(np.vdot(bxa,bxa))
1000 1 tkerber
        cxb  = np.cross(c,b)
1001 1 tkerber
        cxb /= np.sqrt(np.vdot(cxb,cxb))
1002 1 tkerber
        angle = np.vdot(bxa,cxb)
1003 1 tkerber
        # check for numerical trouble due to finite precision:
1004 1 tkerber
        if angle < -1: angle = -1
1005 1 tkerber
        if angle >  1: angle =  1
1006 1 tkerber
        angle = np.arccos(angle)
1007 1 tkerber
        if (np.vdot(bxa,c)) > 0: angle = 2*np.pi-angle
1008 1 tkerber
        return angle
1009 1 tkerber
1010 1 tkerber
    def set_dihedral(self,list,angle,mask=None):
1011 1 tkerber
        """
1012 1 tkerber
        set the dihedral angle between vectors list[0]->list[1] and
1013 1 tkerber
        list[2]->list[3] by changing the atom indexed by list[3]
1014 1 tkerber
        if mask is not None, all the atoms described in mask
1015 1 tkerber
        (read: the entire subgroup) are moved
1016 1 tkerber

1017 1 tkerber
        example: the following defines a very crude
1018 1 tkerber
        ethane-like molecule and twists one half of it by 30 degrees.
1019 1 tkerber

1020 1 tkerber
        >>> atoms = Atoms('HHCCHH',[[-1,1,0],[-1,-1,0],[0,0,0],[1,0,0],[2,1,0],[2,-1,0]])
1021 1 tkerber
        >>> atoms.set_dihedral([1,2,3,4],7*pi/6,mask=[0,0,0,1,1,1])
1022 1 tkerber
        """
1023 1 tkerber
        # if not provided, set mask to the last atom in the dihedral description
1024 1 tkerber
        if mask is None:
1025 1 tkerber
            mask = np.zeros(len(self))
1026 1 tkerber
            mask[list[3]] = 1
1027 1 tkerber
        # compute necessary in dihedral change, from current value
1028 1 tkerber
        current =self.get_dihedral(list)
1029 1 tkerber
        diff    = angle - current
1030 1 tkerber
        # do rotation of subgroup by copying it to temporary atoms object and then rotating that
1031 1 tkerber
        axis   = self.positions[list[2]]-self.positions[list[1]]
1032 1 tkerber
        center = self.positions[list[2]]
1033 1 tkerber
        # recursive object definition might not be the most elegant thing, more generally useful might be a rotation function with a mask?
1034 1 tkerber
        group  = self.__class__()
1035 1 tkerber
        for i in range(len(self)):
1036 1 tkerber
            if mask[i]:
1037 1 tkerber
                group += self[i]
1038 1 tkerber
        group.translate(-center)
1039 1 tkerber
        group.rotate(axis,diff)
1040 1 tkerber
        group.translate(center)
1041 1 tkerber
        # set positions in original atoms object
1042 1 tkerber
        j = 0
1043 1 tkerber
        for i in range(len(self)):
1044 1 tkerber
            if mask[i]:
1045 1 tkerber
                self.positions[i] = group[j].get_position()
1046 1 tkerber
                j += 1
1047 1 tkerber
1048 1 tkerber
    def rotate_dihedral(self,list,angle,mask=None):
1049 1 tkerber
        """ complementing the two routines above: rotate a group by a predefined dihedral angle,
1050 1 tkerber
        starting from its current configuration
1051 1 tkerber
        """
1052 1 tkerber
        start = self.get_dihedral(list)
1053 1 tkerber
        self.set_dihedral(list,angle+start,mask)
1054 1 tkerber
1055 1 tkerber
    def rattle(self, stdev=0.001, seed=42):
1056 1 tkerber
        """Randomly displace atoms.
1057 1 tkerber

1058 1 tkerber
        This method adds random displacements to the atomic positions,
1059 1 tkerber
        taking a possible constraint into account.  The random numbers are
1060 1 tkerber
        drawn from a normal distribution of standard deviation stdev.
1061 1 tkerber

1062 1 tkerber
        For a parallel calculation, it is important to use the same
1063 1 tkerber
        seed on all processors!  """
1064 1 tkerber
1065 1 tkerber
        rs = np.random.RandomState(seed)
1066 1 tkerber
        positions = self.arrays['positions']
1067 1 tkerber
        self.set_positions(positions +
1068 1 tkerber
                           rs.normal(scale=stdev, size=positions.shape))
1069 1 tkerber
1070 1 tkerber
    def get_distance(self, a0, a1, mic=False):
1071 1 tkerber
        """Return distance between two atoms.
1072 1 tkerber

1073 1 tkerber
        Use mic=True to use the Minimum Image Convention.
1074 1 tkerber
        """
1075 1 tkerber
1076 1 tkerber
        R = self.arrays['positions']
1077 1 tkerber
        D = R[a1] - R[a0]
1078 1 tkerber
        if mic:
1079 1 tkerber
            raise NotImplemented  # XXX
1080 1 tkerber
        return np.linalg.norm(D)
1081 1 tkerber
1082 1 tkerber
    def set_distance(self, a0, a1, distance, fix=0.5):
1083 1 tkerber
        """Set the distance between two atoms.
1084 1 tkerber

1085 1 tkerber
        Set the distance between atoms *a0* and *a1* to *distance*.
1086 1 tkerber
        By default, the center of the two atoms will be fixed.  Use
1087 1 tkerber
        *fix=0* to fix the first atom, *fix=1* to fix the second
1088 1 tkerber
        atom and *fix=0.5* (default) to fix the center of the bond."""
1089 1 tkerber
1090 1 tkerber
        R = self.arrays['positions']
1091 1 tkerber
        D = R[a1] - R[a0]
1092 1 tkerber
        x = 1.0 - distance / np.linalg.norm(D)
1093 1 tkerber
        R[a0] += (x * fix) * D
1094 1 tkerber
        R[a1] -= (x * (1.0 - fix)) * D
1095 1 tkerber
1096 1 tkerber
    def get_scaled_positions(self):
1097 1 tkerber
        """Get positions relative to unit cell.
1098 1 tkerber

1099 1 tkerber
        Atoms outside the unit cell will be wrapped into the cell in
1100 1 tkerber
        those directions with periodic boundary conditions so that the
1101 1 tkerber
        scaled coordinates are beween zero and one."""
1102 1 tkerber
1103 1 tkerber
        scaled = np.linalg.solve(self._cell.T, self.arrays['positions'].T).T
1104 1 tkerber
        for i in range(3):
1105 1 tkerber
            if self._pbc[i]:
1106 1 tkerber
                # Yes, we need to do it twice.
1107 1 tkerber
                # See the scaled_positions.py test
1108 1 tkerber
                scaled[:, i] %= 1.0
1109 1 tkerber
                scaled[:, i] %= 1.0
1110 1 tkerber
        return scaled
1111 1 tkerber
1112 1 tkerber
    def set_scaled_positions(self, scaled):
1113 1 tkerber
        """Set positions relative to unit cell."""
1114 1 tkerber
        self.arrays['positions'][:] = np.dot(scaled, self._cell)
1115 1 tkerber
1116 1 tkerber
    def get_temperature(self):
1117 1 tkerber
        """Get the temperature. in Kelvin"""
1118 1 tkerber
        ekin = self.get_kinetic_energy() / len(self)
1119 1 tkerber
        return ekin /(1.5*units.kB)
1120 1 tkerber
1121 1 tkerber
    def get_isotropic_pressure(self, stress):
1122 1 tkerber
        """ get the current calculated pressure, assume isotropic medium.
1123 1 tkerber
            in Bar
1124 1 tkerber
        """
1125 1 tkerber
        if type(stress) == type(1.0) or type(stress) == type(1):
1126 1 tkerber
            return -stress * 1e-5 / units.Pascal
1127 1 tkerber
        elif stress.shape == (3,3):
1128 1 tkerber
            return (-(stress[0, 0] + stress[1, 1] + stress[2, 2]) / 3.0) * \
1129 1 tkerber
                    1e-5 / units.Pascal
1130 1 tkerber
        elif stress.shape == (6,):
1131 1 tkerber
            return (-(stress[0] + stress[1] + stress[2]) / 3.0) * \
1132 1 tkerber
                   1e-5 / units.Pascal
1133 1 tkerber
        else:
1134 1 tkerber
            raise ValueError, "The external stress has the wrong shape."
1135 1 tkerber
1136 1 tkerber
1137 1 tkerber
1138 1 tkerber
    def __eq__(self, other):
1139 1 tkerber
        """Check for identity of two atoms objects.
1140 1 tkerber

1141 1 tkerber
        Identity means: same positions, atomic numbers, unit cell and
1142 1 tkerber
        periodic boundary conditions."""
1143 1 tkerber
        try:
1144 1 tkerber
            a = self.arrays
1145 1 tkerber
            b = other.arrays
1146 1 tkerber
            return (len(self) == len(other) and
1147 1 tkerber
                    (a['positions'] == b['positions']).all() and
1148 1 tkerber
                    (a['numbers'] == b['numbers']).all() and
1149 1 tkerber
                    (self._cell == other.cell).all() and
1150 1 tkerber
                    (self._pbc == other.pbc).all())
1151 1 tkerber
        except AttributeError:
1152 1 tkerber
            return NotImplemented
1153 1 tkerber
1154 1 tkerber
    def __ne__(self, other):
1155 1 tkerber
        eq = self.__eq__(other)
1156 1 tkerber
        if eq is NotImplemented:
1157 1 tkerber
            return eq
1158 1 tkerber
        else:
1159 1 tkerber
            return not eq
1160 1 tkerber
1161 1 tkerber
    __hash__ = None
1162 1 tkerber
1163 1 tkerber
    def get_volume(self):
1164 1 tkerber
        """Get volume of unit cell."""
1165 1 tkerber
        return abs(np.linalg.det(self._cell))
1166 1 tkerber
1167 1 tkerber
    def _get_positions(self):
1168 1 tkerber
        """Return reference to positions-array for inplace manipulations."""
1169 1 tkerber
        return self.arrays['positions']
1170 1 tkerber
1171 1 tkerber
    def _set_positions(self, pos):
1172 1 tkerber
        """Set positions directly, bypassing constraints."""
1173 1 tkerber
        self.arrays['positions'][:] = pos
1174 1 tkerber
1175 1 tkerber
    positions = property(_get_positions, _set_positions,
1176 1 tkerber
                         doc='Attribute for direct ' +
1177 1 tkerber
                         'manipulation of the positions.')
1178 1 tkerber
1179 1 tkerber
    def _get_atomic_numbers(self):
1180 1 tkerber
        """Return reference to atomic numbers for inplace manipulations."""
1181 1 tkerber
        return self.arrays['numbers']
1182 1 tkerber
1183 1 tkerber
    numbers = property(_get_atomic_numbers, set_atomic_numbers,
1184 1 tkerber
                       doc='Attribute for direct ' +
1185 1 tkerber
                       'manipulation of the atomic numbers.')
1186 1 tkerber
1187 1 tkerber
    def _get_cell(self):
1188 1 tkerber
        """Return reference to unit cell for inplace manipulations."""
1189 1 tkerber
        return self._cell
1190 1 tkerber
1191 1 tkerber
    cell = property(_get_cell, set_cell, doc='Attribute for direct ' +
1192 1 tkerber
                       'manipulation of the unit cell.')
1193 1 tkerber
1194 1 tkerber
    def _get_pbc(self):
1195 1 tkerber
        """Return reference to pbc-flags for inplace manipulations."""
1196 1 tkerber
        return self._pbc
1197 1 tkerber
1198 1 tkerber
    pbc = property(_get_pbc, set_pbc,
1199 1 tkerber
                   doc='Attribute for direct manipulation ' +
1200 1 tkerber
                   'of the periodic boundary condition flags.')
1201 1 tkerber
1202 1 tkerber
    def get_name(self):
1203 1 tkerber
        """Return a name extracted from the elements."""
1204 1 tkerber
        elements = {}
1205 1 tkerber
        for a in self:
1206 1 tkerber
            try:
1207 1 tkerber
                elements[a.symbol] += 1
1208 1 tkerber
            except:
1209 1 tkerber
                elements[a.symbol] = 1
1210 1 tkerber
        name = ''
1211 1 tkerber
        for element in elements:
1212 1 tkerber
            name += element
1213 1 tkerber
            if elements[element] > 1:
1214 1 tkerber
                name += str(elements[element])
1215 1 tkerber
        return name
1216 1 tkerber
1217 1 tkerber
    def write(self, filename, format=None):
1218 1 tkerber
        """Write yourself to a file."""
1219 1 tkerber
        from ase.io import write
1220 1 tkerber
        write(filename, self, format)
1221 1 tkerber
1222 1 tkerber
def string2symbols(s):
1223 1 tkerber
    """Convert string to list of chemical symbols."""
1224 1 tkerber
    n = len(s)
1225 1 tkerber
1226 1 tkerber
    if n == 0:
1227 1 tkerber
        return []
1228 1 tkerber
1229 1 tkerber
    c = s[0]
1230 1 tkerber
1231 1 tkerber
    if c.isdigit():
1232 1 tkerber
        i = 1
1233 1 tkerber
        while i < n and s[i].isdigit():
1234 1 tkerber
            i += 1
1235 1 tkerber
        return int(s[:i]) * string2symbols(s[i:])
1236 1 tkerber
1237 1 tkerber
    if c == '(':
1238 1 tkerber
        p = 0
1239 1 tkerber
        for i, c in enumerate(s):
1240 1 tkerber
            if c == '(':
1241 1 tkerber
                p += 1
1242 1 tkerber
            elif c == ')':
1243 1 tkerber
                p -= 1
1244 1 tkerber
                if p == 0:
1245 1 tkerber
                    break
1246 1 tkerber
        j = i + 1
1247 1 tkerber
        while j < n and s[j].isdigit():
1248 1 tkerber
            j += 1
1249 1 tkerber
        if j > i + 1:
1250 1 tkerber
            m = int(s[i + 1:j])
1251 1 tkerber
        else:
1252 1 tkerber
            m = 1
1253 1 tkerber
        return m * string2symbols(s[1:i]) + string2symbols(s[j:])
1254 1 tkerber
1255 1 tkerber
    if c.isupper():
1256 1 tkerber
        i = 1
1257 1 tkerber
        if 1 < n and s[1].islower():
1258 1 tkerber
            i += 1
1259 1 tkerber
        j = i
1260 1 tkerber
        while j < n and s[j].isdigit():
1261 1 tkerber
            j += 1
1262 1 tkerber
        if j > i:
1263 1 tkerber
            m = int(s[i:j])
1264 1 tkerber
        else:
1265 1 tkerber
            m = 1
1266 1 tkerber
        return m * [s[:i]] + string2symbols(s[j:])
1267 1 tkerber
    else:
1268 1 tkerber
        raise ValueError
1269 1 tkerber
1270 1 tkerber
def symbols2numbers(symbols):
1271 1 tkerber
    if isinstance(symbols, str):
1272 1 tkerber
        symbols = string2symbols(symbols)
1273 1 tkerber
    numbers = []
1274 1 tkerber
    for s in symbols:
1275 1 tkerber
        if isinstance(s, str):
1276 1 tkerber
            numbers.append(atomic_numbers[s])
1277 1 tkerber
        else:
1278 1 tkerber
            numbers.append(s)
1279 1 tkerber
    return numbers
1280 1 tkerber
1281 1 tkerber
def string2vector(v):
1282 1 tkerber
    if isinstance(v, str):
1283 1 tkerber
        if v[0] == '-':
1284 1 tkerber
            return -string2vector(v[1:])
1285 1 tkerber
        w = np.zeros(3)
1286 1 tkerber
        w['xyz'.index(v)] = 1.0
1287 1 tkerber
        return w
1288 1 tkerber
    return np.array(v, float)
1289 1 tkerber
1290 1 tkerber
def default(data, dflt):
1291 1 tkerber
    """Helper function for setting default values."""
1292 1 tkerber
    if data is None:
1293 1 tkerber
        return None
1294 1 tkerber
    elif isinstance(data, (list, tuple)):
1295 1 tkerber
        newdata = []
1296 1 tkerber
        allnone = True
1297 1 tkerber
        for x in data:
1298 1 tkerber
            if x is None:
1299 1 tkerber
                newdata.append(dflt)
1300 1 tkerber
            else:
1301 1 tkerber
                newdata.append(x)
1302 1 tkerber
                allnone = False
1303 1 tkerber
        if allnone:
1304 1 tkerber
            return None
1305 1 tkerber
        return newdata
1306 1 tkerber
    else:
1307 1 tkerber
        return data