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
1"""This module defines the Atom object."""
3import numpy as np
5from ase.data import atomic_numbers, chemical_symbols, atomic_masses
7# Singular, plural, default value:
8names = {'position': ('positions', np.zeros(3)),
9 'number': ('numbers', 0),
10 'tag': ('tags', 0),
11 'momentum': ('momenta', np.zeros(3)),
12 'mass': ('masses', None),
13 'magmom': ('initial_magmoms', 0.0),
14 'charge': ('initial_charges', 0.0)}
17def atomproperty(name, doc):
18 """Helper function to easily create Atom attribute property."""
20 def getter(self):
21 return self.get(name)
23 def setter(self, value):
24 self.set(name, value)
26 def deleter(self):
27 self.delete(name)
29 return property(getter, setter, deleter, doc)
32def abcproperty(index):
33 """Helper function to easily create Atom ABC-property."""
35 def getter(self):
36 return self.scaled_position[index]
38 def setter(self, value):
39 # We can't just do self.scaled_position[i] = value
40 # because scaled_position is a new buffer, not a view into
41 # something we can write back to.
42 # This is a clear bug!
43 spos = self.scaled_position
44 spos[index] = value
45 self.scaled_position = spos
47 return property(getter, setter, doc='ABC'[index] + '-coordinate')
50def xyzproperty(index):
51 """Helper function to easily create Atom XYZ-property."""
53 def getter(self):
54 return self.position[index]
56 def setter(self, value):
57 self.position[index] = value
59 return property(getter, setter, doc='XYZ'[index] + '-coordinate')
62class Atom:
63 """Class for representing a single atom.
65 Parameters:
67 symbol: str or int
68 Can be a chemical symbol (str) or an atomic number (int).
69 position: sequence of 3 floats
70 Atomic position.
71 tag: int
72 Special purpose tag.
73 momentum: sequence of 3 floats
74 Momentum for atom.
75 mass: float
76 Atomic mass in atomic units.
77 magmom: float or 3 floats
78 Magnetic moment.
79 charge: float
80 Atomic charge.
81 """
82 __slots__ = ['data', 'atoms', 'index']
84 def __init__(self, symbol='X', position=(0, 0, 0),
85 tag=None, momentum=None, mass=None,
86 magmom=None, charge=None,
87 atoms=None, index=None):
89 self.data = d = {}
91 if atoms is None:
92 # This atom is not part of any Atoms object:
93 if isinstance(symbol, str):
94 d['number'] = atomic_numbers[symbol]
95 else:
96 d['number'] = symbol
97 d['position'] = np.array(position, float)
98 d['tag'] = tag
99 if momentum is not None:
100 momentum = np.array(momentum, float)
101 d['momentum'] = momentum
102 d['mass'] = mass
103 if magmom is not None:
104 magmom = np.array(magmom, float)
105 d['magmom'] = magmom
106 d['charge'] = charge
108 self.index = index
109 self.atoms = atoms
111 @property
112 def scaled_position(self):
113 pos = self.position
114 spos = self.atoms.cell.scaled_positions(pos[np.newaxis])
115 return spos[0]
117 @scaled_position.setter
118 def scaled_position(self, value):
119 pos = self.atoms.cell.cartesian_positions(value)
120 self.position = pos
122 def __repr__(self):
123 s = "Atom('%s', %s" % (self.symbol, list(self.position))
124 for name in ['tag', 'momentum', 'mass', 'magmom', 'charge']:
125 value = self.get_raw(name)
126 if value is not None:
127 if isinstance(value, np.ndarray):
128 value = value.tolist()
129 s += ', %s=%s' % (name, value)
130 if self.atoms is None:
131 s += ')'
132 else:
133 s += ', index=%d)' % self.index
134 return s
136 def cut_reference_to_atoms(self):
137 """Cut reference to atoms object."""
138 for name in names:
139 self.data[name] = self.get_raw(name)
140 self.index = None
141 self.atoms = None
143 def get_raw(self, name):
144 """Get name attribute, return None if not explicitly set."""
145 if name == 'symbol':
146 return chemical_symbols[self.get_raw('number')]
148 if self.atoms is None:
149 return self.data[name]
151 plural = names[name][0]
152 if plural in self.atoms.arrays:
153 return self.atoms.arrays[plural][self.index]
154 else:
155 return None
157 def get(self, name):
158 """Get name attribute, return default if not explicitly set."""
159 value = self.get_raw(name)
160 if value is None:
161 if name == 'mass':
162 value = atomic_masses[self.number]
163 else:
164 value = names[name][1]
165 return value
167 def set(self, name, value):
168 """Set name attribute to value."""
169 if name == 'symbol':
170 name = 'number'
171 value = atomic_numbers[value]
173 if self.atoms is None:
174 assert name in names
175 self.data[name] = value
176 else:
177 plural, default = names[name]
178 if plural in self.atoms.arrays:
179 array = self.atoms.arrays[plural]
180 if name == 'magmom' and array.ndim == 2:
181 assert len(value) == 3
182 array[self.index] = value
183 else:
184 if name == 'magmom' and np.asarray(value).ndim == 1:
185 array = np.zeros((len(self.atoms), 3))
186 elif name == 'mass':
187 array = self.atoms.get_masses()
188 else:
189 default = np.asarray(default)
190 array = np.zeros((len(self.atoms),) + default.shape,
191 default.dtype)
192 array[self.index] = value
193 self.atoms.new_array(plural, array)
195 def delete(self, name):
196 """Delete name attribute."""
197 assert self.atoms is None
198 assert name not in ['number', 'symbol', 'position']
199 self.data[name] = None
201 symbol = atomproperty('symbol', 'Chemical symbol')
202 number = atomproperty('number', 'Atomic number')
203 position = atomproperty('position', 'XYZ-coordinates')
204 tag = atomproperty('tag', 'Integer tag')
205 momentum = atomproperty('momentum', 'XYZ-momentum')
206 mass = atomproperty('mass', 'Atomic mass')
207 magmom = atomproperty('magmom', 'Initial magnetic moment')
208 charge = atomproperty('charge', 'Initial atomic charge')
209 x = xyzproperty(0)
210 y = xyzproperty(1)
211 z = xyzproperty(2)
213 a = abcproperty(0)
214 b = abcproperty(1)
215 c = abcproperty(2)