Statistiques
| Révision :

root / ase / io / trajectory.py @ 15

Historique | Voir | Annoter | Télécharger (11,7 ko)

1 1 tkerber
import os
2 1 tkerber
import cPickle as pickle
3 1 tkerber
4 1 tkerber
from ase.calculators.singlepoint import SinglePointCalculator
5 1 tkerber
from ase.atoms import Atoms
6 1 tkerber
from ase.parallel import rank, barrier
7 1 tkerber
from ase.utils import devnull
8 1 tkerber
9 1 tkerber
10 1 tkerber
class PickleTrajectory:
11 1 tkerber
    "Reads/writes Atoms objects into a .traj file."
12 1 tkerber
    # Per default, write these quantities
13 1 tkerber
    write_energy = True
14 1 tkerber
    write_forces = True
15 1 tkerber
    write_stress = True
16 1 tkerber
    write_magmoms = True
17 1 tkerber
    write_momenta = True
18 1 tkerber
19 1 tkerber
    def __init__(self, filename, mode='r', atoms=None, master=None,
20 1 tkerber
                 backup=True):
21 1 tkerber
        """A PickleTrajectory can be created in read, write or append mode.
22 1 tkerber

23 1 tkerber
        Parameters:
24 1 tkerber

25 1 tkerber
        filename:
26 1 tkerber
            The name of the parameter file.  Should end in .traj.
27 1 tkerber

28 1 tkerber
        mode='r':
29 1 tkerber
            The mode.
30 1 tkerber

31 1 tkerber
            'r' is read mode, the file should already exist, and
32 1 tkerber
            no atoms argument should be specified.
33 1 tkerber

34 1 tkerber
            'w' is write mode.  If the file already exists, is it
35 1 tkerber
            renamed by appending .bak to the file name.  The atoms
36 1 tkerber
            argument specifies the Atoms object to be written to the
37 1 tkerber
            file, if not given it must instead be given as an argument
38 1 tkerber
            to the write() method.
39 1 tkerber

40 1 tkerber
            'a' is append mode.  It acts a write mode, except that
41 1 tkerber
            data is appended to a preexisting file.
42 1 tkerber

43 1 tkerber
        atoms=None:
44 1 tkerber
            The Atoms object to be written in write or append mode.
45 1 tkerber

46 1 tkerber
        master=None:
47 1 tkerber
            Controls which process does the actual writing. The
48 1 tkerber
            default is that process number 0 does this.  If this
49 1 tkerber
            argument is given, processes where it is True will write.
50 1 tkerber

51 1 tkerber
        backup=True:
52 1 tkerber
            Use backup=False to disable renaming of an existing file.
53 1 tkerber
        """
54 1 tkerber
        self.offsets = []
55 1 tkerber
        if master is None:
56 1 tkerber
            master = (rank == 0)
57 1 tkerber
        self.master = master
58 1 tkerber
        self.backup = backup
59 1 tkerber
        self.set_atoms(atoms)
60 1 tkerber
        self.open(filename, mode)
61 1 tkerber
62 1 tkerber
    def open(self, filename, mode):
63 1 tkerber
        """Opens the file.
64 1 tkerber

65 1 tkerber
        For internal use only.
66 1 tkerber
        """
67 1 tkerber
        self.fd = filename
68 1 tkerber
        if mode == 'r':
69 1 tkerber
            if isinstance(filename, str):
70 1 tkerber
                self.fd = open(filename, 'rb')
71 1 tkerber
            self.read_header()
72 1 tkerber
        elif mode == 'a':
73 1 tkerber
            exists = True
74 1 tkerber
            if isinstance(filename, str):
75 1 tkerber
                exists = os.path.isfile(filename)
76 1 tkerber
                if exists:
77 1 tkerber
                    self.fd = open(filename, 'rb')
78 1 tkerber
                    self.read_header()
79 1 tkerber
                    self.fd.close()
80 1 tkerber
                barrier()
81 1 tkerber
                if self.master:
82 1 tkerber
                    self.fd = open(filename, 'ab+')
83 1 tkerber
                else:
84 1 tkerber
                    self.fd = devnull
85 1 tkerber
        elif mode == 'w':
86 1 tkerber
            if self.master:
87 1 tkerber
                if isinstance(filename, str):
88 1 tkerber
                    if self.backup and os.path.isfile(filename):
89 1 tkerber
                        os.rename(filename, filename + '.bak')
90 1 tkerber
                    self.fd = open(filename, 'wb')
91 1 tkerber
            else:
92 1 tkerber
                self.fd = devnull
93 1 tkerber
        else:
94 1 tkerber
            raise ValueError('mode must be "r", "w" or "a".')
95 1 tkerber
96 1 tkerber
    def set_atoms(self, atoms=None):
97 1 tkerber
        """Associate an Atoms object with the trajectory.
98 1 tkerber

99 1 tkerber
        Mostly for internal use.
100 1 tkerber
        """
101 1 tkerber
        if atoms is not None and not hasattr(atoms, 'get_positions'):
102 1 tkerber
            raise TypeError('"atoms" argument is not an Atoms object.')
103 1 tkerber
        self.atoms = atoms
104 1 tkerber
105 1 tkerber
    def read_header(self):
106 1 tkerber
        self.fd.seek(0)
107 1 tkerber
        try:
108 1 tkerber
            if self.fd.read(len('PickleTrajectory')) != 'PickleTrajectory':
109 1 tkerber
                raise IOError('This is not a trajectory file!')
110 1 tkerber
            d = pickle.load(self.fd)
111 1 tkerber
        except EOFError:
112 1 tkerber
            raise EOFError('Bad trajectory file.')
113 1 tkerber
        self.pbc = d['pbc']
114 1 tkerber
        self.numbers = d['numbers']
115 1 tkerber
        self.tags = d.get('tags')
116 1 tkerber
        self.masses = d.get('masses')
117 1 tkerber
        self.constraints = d['constraints']
118 1 tkerber
        self.offsets.append(self.fd.tell())
119 1 tkerber
120 1 tkerber
    def write(self, atoms=None):
121 1 tkerber
        """Write the atoms to the file.
122 1 tkerber

123 1 tkerber
        If the atoms argument is not given, the atoms object specified
124 1 tkerber
        when creating the trajectory object is used.
125 1 tkerber
        """
126 1 tkerber
        if atoms is None:
127 1 tkerber
            atoms = self.atoms
128 1 tkerber
129 1 tkerber
        if hasattr(atoms, 'interpolate'):
130 1 tkerber
            # seems to be a NEB
131 1 tkerber
            neb = atoms
132 1 tkerber
            try:
133 1 tkerber
                neb.get_energies_and_forces(all=True)
134 1 tkerber
            except AttributeError:
135 1 tkerber
                pass
136 1 tkerber
            for image in neb.images:
137 1 tkerber
                self.write(image)
138 1 tkerber
            return
139 1 tkerber
140 1 tkerber
        if len(self.offsets) == 0:
141 1 tkerber
            self.write_header(atoms)
142 1 tkerber
143 1 tkerber
        if atoms.has('momenta'):
144 1 tkerber
            momenta = atoms.get_momenta()
145 1 tkerber
        else:
146 1 tkerber
            momenta = None
147 1 tkerber
148 1 tkerber
        d = {'positions': atoms.get_positions(),
149 1 tkerber
             'cell': atoms.get_cell(),
150 1 tkerber
             'momenta': momenta}
151 1 tkerber
152 1 tkerber
        if atoms.get_calculator() is not None:
153 1 tkerber
            if self.write_energy:
154 1 tkerber
                d['energy'] = atoms.get_potential_energy()
155 1 tkerber
            if self.write_forces:
156 1 tkerber
                assert self.write_energy
157 1 tkerber
                try:
158 1 tkerber
                    d['forces'] = atoms.get_forces(apply_constraint=False)
159 1 tkerber
                except NotImplementedError:
160 1 tkerber
                    pass
161 1 tkerber
            if self.write_stress:
162 1 tkerber
                assert self.write_energy
163 1 tkerber
                try:
164 1 tkerber
                    d['stress'] = atoms.get_stress()
165 1 tkerber
                except NotImplementedError:
166 1 tkerber
                    pass
167 1 tkerber
168 1 tkerber
            if self.write_magmoms:
169 1 tkerber
                try:
170 1 tkerber
                    if atoms.calc.get_spin_polarized():
171 1 tkerber
                        d['magmoms'] = atoms.get_magnetic_moments()
172 1 tkerber
                except (NotImplementedError, AttributeError):
173 1 tkerber
                    pass
174 1 tkerber
175 1 tkerber
        if 'magmoms' not in d and atoms.has('magmoms'):
176 1 tkerber
            d['magmoms'] = atoms.get_initial_magnetic_moments()
177 1 tkerber
178 1 tkerber
        if self.master:
179 1 tkerber
            pickle.dump(d, self.fd, protocol=-1)
180 1 tkerber
        self.fd.flush()
181 1 tkerber
        self.offsets.append(self.fd.tell())
182 1 tkerber
183 1 tkerber
    def write_header(self, atoms):
184 1 tkerber
        self.fd.write('PickleTrajectory')
185 1 tkerber
        if atoms.has('tags'):
186 1 tkerber
            tags = atoms.get_tags()
187 1 tkerber
        else:
188 1 tkerber
            tags = None
189 1 tkerber
        if atoms.has('masses'):
190 1 tkerber
            masses = atoms.get_masses()
191 1 tkerber
        else:
192 1 tkerber
            masses = None
193 1 tkerber
        d = {'pbc': atoms.get_pbc(),
194 1 tkerber
             'numbers': atoms.get_atomic_numbers(),
195 1 tkerber
             'tags': tags,
196 1 tkerber
             'masses': masses,
197 1 tkerber
             'constraints': atoms.constraints}
198 1 tkerber
        pickle.dump(d, self.fd, protocol=-1)
199 1 tkerber
        self.header_written = True
200 1 tkerber
        self.offsets.append(self.fd.tell())
201 1 tkerber
202 1 tkerber
    def close(self):
203 1 tkerber
        """Close the trajectory file."""
204 1 tkerber
        self.fd.close()
205 1 tkerber
206 1 tkerber
    def __getitem__(self, i=-1):
207 1 tkerber
        N = len(self.offsets)
208 1 tkerber
        if 0 <= i < N:
209 1 tkerber
            self.fd.seek(self.offsets[i])
210 1 tkerber
            try:
211 1 tkerber
                d = pickle.load(self.fd)
212 1 tkerber
            except EOFError:
213 1 tkerber
                raise IndexError
214 1 tkerber
            if i == N - 1:
215 1 tkerber
                self.offsets.append(self.fd.tell())
216 1 tkerber
            try:
217 1 tkerber
                magmoms = d['magmoms']
218 1 tkerber
            except KeyError:
219 1 tkerber
                magmoms = None
220 1 tkerber
            atoms = Atoms(positions=d['positions'],
221 1 tkerber
                          numbers=self.numbers,
222 1 tkerber
                          cell=d['cell'],
223 1 tkerber
                          momenta=d['momenta'],
224 1 tkerber
                          magmoms=magmoms,
225 1 tkerber
                          tags=self.tags,
226 1 tkerber
                          masses=self.masses,
227 1 tkerber
                          pbc=self.pbc,
228 1 tkerber
                          constraint=[c.copy() for c in self.constraints])
229 1 tkerber
            if 'energy' in d:
230 1 tkerber
                calc = SinglePointCalculator(
231 1 tkerber
                    d.get('energy', None), d.get('forces', None),
232 1 tkerber
                    d.get('stress', None), magmoms, atoms)
233 1 tkerber
                atoms.set_calculator(calc)
234 1 tkerber
            return atoms
235 1 tkerber
236 1 tkerber
        if i >= N:
237 1 tkerber
            for j in range(N - 1, i + 1):
238 1 tkerber
                atoms = self[j]
239 1 tkerber
            return atoms
240 1 tkerber
241 1 tkerber
        i = len(self) + i
242 1 tkerber
        if i < 0:
243 1 tkerber
            raise IndexError('Trajectory index out of range.')
244 1 tkerber
        return self[i]
245 1 tkerber
246 1 tkerber
    def __len__(self):
247 1 tkerber
        N = len(self.offsets) - 1
248 1 tkerber
        while True:
249 1 tkerber
            self.fd.seek(self.offsets[N])
250 1 tkerber
            try:
251 1 tkerber
                pickle.load(self.fd)
252 1 tkerber
            except EOFError:
253 1 tkerber
                return N
254 1 tkerber
            self.offsets.append(self.fd.tell())
255 1 tkerber
            N += 1
256 1 tkerber
257 1 tkerber
    def __iter__(self):
258 1 tkerber
        del self.offsets[1:]
259 1 tkerber
        return self
260 1 tkerber
261 1 tkerber
    def next(self):
262 1 tkerber
        try:
263 1 tkerber
            return self[len(self.offsets) - 1]
264 1 tkerber
        except IndexError:
265 1 tkerber
            raise StopIteration
266 1 tkerber
267 1 tkerber
    def guess_offsets(self):
268 1 tkerber
        size = os.path.getsize(self.fd.name)
269 1 tkerber
270 1 tkerber
        while True:
271 1 tkerber
            self.fd.seek(self.offsets[-1])
272 1 tkerber
            try:
273 1 tkerber
                pickle.load(self.fd)
274 1 tkerber
            except:
275 1 tkerber
                raise EOFError('Damaged trajectory file.')
276 1 tkerber
            else:
277 1 tkerber
                self.offsets.append(self.fd.tell())
278 1 tkerber
279 1 tkerber
            if self.offsets[-1] >= size:
280 1 tkerber
                break
281 1 tkerber
282 1 tkerber
            if len(self.offsets) > 2:
283 1 tkerber
                step1 = self.offsets[-1] - self.offsets[-2]
284 1 tkerber
                step2 = self.offsets[-2] - self.offsets[-3]
285 1 tkerber
286 1 tkerber
                if step1 == step2:
287 1 tkerber
                    m = int((size - self.offsets[-1]) / step1) - 1
288 1 tkerber
289 1 tkerber
                    while m > 1:
290 1 tkerber
                        self.fd.seek(self.offsets[-1] + m * step1)
291 1 tkerber
                        try:
292 1 tkerber
                            pickle.load(self.fd)
293 1 tkerber
                        except:
294 1 tkerber
                            m = m / 2
295 1 tkerber
                        else:
296 1 tkerber
                            for i in range(m):
297 1 tkerber
                                self.offsets.append(self.offsets[-1] + step1)
298 1 tkerber
                            m = 0
299 1 tkerber
300 1 tkerber
        return
301 1 tkerber
302 1 tkerber
def read_trajectory(filename, index=-1):
303 1 tkerber
    traj = PickleTrajectory(filename, mode='r')
304 1 tkerber
305 1 tkerber
    if isinstance(index, int):
306 1 tkerber
        return traj[index]
307 1 tkerber
    else:
308 1 tkerber
        # Here, we try to read only the configurations we need to read
309 1 tkerber
        # and len(traj) should only be called if we need to as it will
310 1 tkerber
        # read all configurations!
311 1 tkerber
312 1 tkerber
        # XXX there must be a simpler way?
313 1 tkerber
        step = index.step or 1
314 1 tkerber
        if step > 0:
315 1 tkerber
            start = index.start or 0
316 1 tkerber
            if start < 0:
317 1 tkerber
                start += len(traj)
318 1 tkerber
            stop = index.stop or len(traj)
319 1 tkerber
            if stop < 0:
320 1 tkerber
                stop += len(traj)
321 1 tkerber
        else:
322 1 tkerber
            if index.start is None:
323 1 tkerber
                start = len(traj) - 1
324 1 tkerber
            else:
325 1 tkerber
                start = index.start
326 1 tkerber
                if start < 0:
327 1 tkerber
                    start += len(traj)
328 1 tkerber
            if index.stop is None:
329 1 tkerber
                stop = -1
330 1 tkerber
            else:
331 1 tkerber
                stop = index.stop
332 1 tkerber
                if stop < 0:
333 1 tkerber
                    stop += len(traj)
334 1 tkerber
335 1 tkerber
        return [traj[i] for i in range(start, stop, step)]
336 1 tkerber
337 1 tkerber
def write_trajectory(filename, images):
338 1 tkerber
    """Write image(s) to trajectory.
339 1 tkerber

340 1 tkerber
    Write also energy, forces, and stress if they are already
341 1 tkerber
    calculated."""
342 1 tkerber
343 1 tkerber
    traj = PickleTrajectory(filename, mode='w')
344 1 tkerber
345 1 tkerber
    if not isinstance(images, (list, tuple)):
346 1 tkerber
        images = [images]
347 1 tkerber
348 1 tkerber
    for atoms in images:
349 1 tkerber
        # Avoid potentially expensive calculations:
350 1 tkerber
        calc = atoms.get_calculator()
351 1 tkerber
        if calc is not None:
352 1 tkerber
            if  hasattr(calc, 'calculation_required'):
353 1 tkerber
                if calc.calculation_required(atoms, ['energy']):
354 1 tkerber
                    traj.write_energy = False
355 1 tkerber
                if calc.calculation_required(atoms, ['forces']):
356 1 tkerber
                    traj.write_forces = False
357 1 tkerber
                if calc.calculation_required(atoms, ['stress']):
358 1 tkerber
                    traj.write_stress = False
359 1 tkerber
                if calc.calculation_required(atoms, ['magmoms']):
360 1 tkerber
                    traj.write_magmoms = False
361 1 tkerber
        else:
362 1 tkerber
            traj.write_energy = False
363 1 tkerber
            traj.write_forces = False
364 1 tkerber
            traj.write_stress = False
365 1 tkerber
            traj.write_magmoms = False
366 1 tkerber
367 1 tkerber
        traj.write(atoms)
368 1 tkerber
    traj.close()