Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import numpy as np
3from ase.atoms import Atoms
4from ase.units import Hartree
5from ase.data import atomic_numbers
6from ase.calculators.singlepoint import SinglePointCalculator
7from ase.utils import writer, reader
10@writer
11def write_xsf(fileobj, images, data=None):
12 is_anim = len(images) > 1
14 if is_anim:
15 fileobj.write('ANIMSTEPS %d\n' % len(images))
17 numbers = images[0].get_atomic_numbers()
19 pbc = images[0].get_pbc()
20 npbc = sum(pbc)
21 if pbc[2]:
22 fileobj.write('CRYSTAL\n')
23 assert npbc == 3
24 elif pbc[1]:
25 fileobj.write('SLAB\n')
26 assert npbc == 2
27 elif pbc[0]:
28 fileobj.write('POLYMER\n')
29 assert npbc == 1
30 else:
31 # (Header written as part of image loop)
32 assert npbc == 0
34 cell_variable = False
35 for image in images[1:]:
36 if np.abs(images[0].cell - image.cell).max() > 1e-14:
37 cell_variable = True
38 break
40 for n, atoms in enumerate(images):
41 anim_token = ' %d' % (n + 1) if is_anim else ''
42 if pbc.any():
43 write_cell = (n == 0 or cell_variable)
44 if write_cell:
45 if cell_variable:
46 fileobj.write('PRIMVEC%s\n' % anim_token)
47 else:
48 fileobj.write('PRIMVEC\n')
49 cell = atoms.get_cell()
50 for i in range(3):
51 fileobj.write(' %.14f %.14f %.14f\n' % tuple(cell[i]))
53 fileobj.write('PRIMCOORD%s\n' % anim_token)
54 else:
55 fileobj.write('ATOMS%s\n' % anim_token)
57 # Get the forces if it's not too expensive:
58 calc = atoms.calc
59 if (calc is not None and
60 (hasattr(calc, 'calculation_required') and
61 not calc.calculation_required(atoms, ['forces']))):
62 forces = atoms.get_forces() / Hartree
63 else:
64 forces = None
66 pos = atoms.get_positions()
68 if pbc.any():
69 fileobj.write(' %d 1\n' % len(pos))
70 for a in range(len(pos)):
71 fileobj.write(' %2d' % numbers[a])
72 fileobj.write(' %20.14f %20.14f %20.14f' % tuple(pos[a]))
73 if forces is None:
74 fileobj.write('\n')
75 else:
76 fileobj.write(' %20.14f %20.14f %20.14f\n' % tuple(forces[a]))
78 if data is None:
79 return
81 fileobj.write('BEGIN_BLOCK_DATAGRID_3D\n')
82 fileobj.write(' data\n')
83 fileobj.write(' BEGIN_DATAGRID_3Dgrid#1\n')
85 data = np.asarray(data)
86 if data.dtype == complex:
87 data = np.abs(data)
89 shape = data.shape
90 fileobj.write(' %d %d %d\n' % shape)
92 cell = atoms.get_cell()
93 origin = np.zeros(3)
94 for i in range(3):
95 if not pbc[i]:
96 origin += cell[i] / shape[i]
97 fileobj.write(' %f %f %f\n' % tuple(origin))
99 for i in range(3):
100 # XXXX is this not just supposed to be the cell?
101 # What's with the strange division?
102 # This disagrees with the output of Octopus. Investigate
103 fileobj.write(' %f %f %f\n' %
104 tuple(cell[i] * (shape[i] + 1) / shape[i]))
106 for k in range(shape[2]):
107 for j in range(shape[1]):
108 fileobj.write(' ')
109 fileobj.write(' '.join(['%f' % d for d in data[:, j, k]]))
110 fileobj.write('\n')
111 fileobj.write('\n')
113 fileobj.write(' END_DATAGRID_3D\n')
114 fileobj.write('END_BLOCK_DATAGRID_3D\n')
117@reader
118def iread_xsf(fileobj, read_data=False):
119 """Yield images and optionally data from xsf file.
121 Yields image1, image2, ..., imageN[, data].
123 Images are Atoms objects and data is a numpy array.
125 Presently supports only a single 3D datagrid."""
126 def _line_generator_func():
127 for line in fileobj:
128 line = line.strip()
129 if not line or line.startswith('#'):
130 continue # Discard comments and empty lines
131 yield line
133 _line_generator = _line_generator_func()
135 def readline():
136 return next(_line_generator)
138 line = readline()
140 if line.startswith('ANIMSTEPS'):
141 nimages = int(line.split()[1])
142 line = readline()
143 else:
144 nimages = 1
146 if line == 'CRYSTAL':
147 pbc = (True, True, True)
148 elif line == 'SLAB':
149 pbc = (True, True, False)
150 elif line == 'POLYMER':
151 pbc = (True, False, False)
152 else:
153 assert line.startswith('ATOMS'), line # can also be ATOMS 1
154 pbc = (False, False, False)
156 cell = None
157 for n in range(nimages):
158 if any(pbc):
159 line = readline()
160 if line.startswith('PRIMCOORD'):
161 assert cell is not None # cell read from previous image
162 else:
163 assert line.startswith('PRIMVEC')
164 cell = []
165 for i in range(3):
166 cell.append([float(x) for x in readline().split()])
168 line = readline()
169 if line.startswith('CONVVEC'): # ignored;
170 for i in range(3):
171 readline()
172 line = readline()
174 assert line.startswith('PRIMCOORD')
175 natoms = int(readline().split()[0])
176 lines = [readline() for _ in range(natoms)]
177 else:
178 assert line.startswith('ATOMS'), line
179 line = readline()
180 lines = []
181 while not (line.startswith('ATOMS') or line.startswith('BEGIN')):
182 lines.append(line)
183 try:
184 line = readline()
185 except StopIteration:
186 break
187 if line.startswith('BEGIN'):
188 # We read "too far" and accidentally got the header
189 # of the data section. This happens only when parsing
190 # ATOMS blocks, because one cannot infer their length.
191 # We will remember the line until later then.
192 data_header_line = line
194 numbers = []
195 positions = []
196 for positionline in lines:
197 tokens = positionline.split()
198 symbol = tokens[0]
199 if symbol.isdigit():
200 numbers.append(int(symbol))
201 else:
202 numbers.append(atomic_numbers[symbol.capitalize()])
203 positions.append([float(x) for x in tokens[1:]])
205 positions = np.array(positions)
206 if len(positions[0]) == 3:
207 forces = None
208 else:
209 forces = positions[:, 3:] * Hartree
210 positions = positions[:, :3]
212 image = Atoms(numbers, positions, cell=cell, pbc=pbc)
214 if forces is not None:
215 image.calc = SinglePointCalculator(image, forces=forces)
216 yield image
218 if read_data:
219 if any(pbc):
220 line = readline()
221 else:
222 line = data_header_line
223 assert line.startswith('BEGIN_BLOCK_DATAGRID_3D')
224 readline() # name
225 line = readline()
226 assert line.startswith('BEGIN_DATAGRID_3D')
228 shape = [int(x) for x in readline().split()]
229 assert len(shape) == 3
230 readline() # start
231 # XXX what to do about these?
233 for i in range(3):
234 readline() # Skip 3x3 matrix for some reason
236 npoints = np.prod(shape)
238 data = []
239 line = readline() # First line of data
240 while not line.startswith('END_DATAGRID_3D'):
241 data.extend([float(x) for x in line.split()])
242 line = readline()
243 assert len(data) == npoints
244 data = np.array(data, float).reshape(shape[::-1]).T
245 # Note that data array is Fortran-ordered
246 yield data
249def read_xsf(fileobj, index=-1, read_data=False):
250 images = list(iread_xsf(fileobj, read_data=read_data))
251 if read_data:
252 array = images[-1]
253 images = images[:-1]
254 return array, images[index]
255 return images[index]