root / ase / calculators / siesta.py @ 20
Historique | Voir | Annoter | Télécharger (24,21 ko)
1 | 1 | tkerber | """This module defines an ASE interface to SIESTA.
|
---|---|---|---|
2 | 1 | tkerber |
|
3 | 1 | tkerber | http://www.uam.es/departamentos/ciencias/fismateriac/siesta
|
4 | 1 | tkerber | """
|
5 | 1 | tkerber | |
6 | 1 | tkerber | import os |
7 | 1 | tkerber | from os.path import join, isfile, islink |
8 | 1 | tkerber | from cmath import exp |
9 | 1 | tkerber | import array |
10 | 1 | tkerber | |
11 | 1 | tkerber | import numpy as np |
12 | 1 | tkerber | |
13 | 1 | tkerber | from ase.data import chemical_symbols |
14 | 1 | tkerber | from ase.units import Rydberg, fs |
15 | 1 | tkerber | |
16 | 1 | tkerber | class Siesta: |
17 | 1 | tkerber | """Class for doing SIESTA calculations.
|
18 | 1 | tkerber |
|
19 | 1 | tkerber | The default parameters are very close to those that the SIESTA
|
20 | 1 | tkerber | Fortran code would use. These are the exceptions::
|
21 | 1 | tkerber |
|
22 | 1 | tkerber | calc = Siesta(label='siesta', xc='LDA', pulay=5, mix=0.1)
|
23 | 1 | tkerber |
|
24 | 1 | tkerber | Use the set_fdf method to set extra FDF parameters::
|
25 | 1 | tkerber |
|
26 | 1 | tkerber | calc.set_fdf('PAO.EnergyShift', 0.01 * Rydberg)
|
27 | 1 | tkerber |
|
28 | 1 | tkerber | """
|
29 | 1 | tkerber | def __init__(self, label='siesta', xc='LDA', kpts=None, nbands=None, |
30 | 1 | tkerber | width=None, meshcutoff=None, charge=None, |
31 | 1 | tkerber | pulay=5, mix=0.1, maxiter=120, |
32 | 1 | tkerber | basis=None, ghosts=[],
|
33 | 1 | tkerber | write_fdf=True):
|
34 | 1 | tkerber | """Construct SIESTA-calculator object.
|
35 | 1 | tkerber |
|
36 | 1 | tkerber | Parameters
|
37 | 1 | tkerber | ==========
|
38 | 1 | tkerber | label: str
|
39 | 1 | tkerber | Prefix to use for filenames (label.fdf, label.txt, ...).
|
40 | 1 | tkerber | Default is 'siesta'.
|
41 | 1 | tkerber | xc: str
|
42 | 1 | tkerber | Exchange-correlation functional. Must be one of LDA, PBE,
|
43 | 1 | tkerber | revPBE, RPBE.
|
44 | 1 | tkerber | kpts: list of three int
|
45 | 1 | tkerber | Monkhost-Pack sampling.
|
46 | 1 | tkerber | nbands: int
|
47 | 1 | tkerber | Number of bands.
|
48 | 1 | tkerber | width: float
|
49 | 1 | tkerber | Fermi-distribution width in eV.
|
50 | 1 | tkerber | meshcutoff: float
|
51 | 1 | tkerber | Cutoff energy in eV for grid.
|
52 | 1 | tkerber | charge: float
|
53 | 1 | tkerber | Total charge of the system.
|
54 | 1 | tkerber | pulay: int
|
55 | 1 | tkerber | Number of old densities to use for Pulay mixing.
|
56 | 1 | tkerber | mix: float
|
57 | 1 | tkerber | Mixing parameter between zero and one for density mixing.
|
58 | 1 | tkerber | write_fdf: bool
|
59 | 1 | tkerber | Use write_fdf=False to use your own fdf-file.
|
60 | 1 | tkerber |
|
61 | 1 | tkerber | Examples
|
62 | 1 | tkerber | ========
|
63 | 1 | tkerber | Use default values:
|
64 | 1 | tkerber |
|
65 | 1 | tkerber | >>> h = Atoms('H', calculator=Siesta())
|
66 | 1 | tkerber | >>> h.center(vacuum=3.0)
|
67 | 1 | tkerber | >>> e = h.get_potential_energy()
|
68 | 1 | tkerber |
|
69 | 1 | tkerber | """
|
70 | 1 | tkerber | |
71 | 1 | tkerber | self.label = label#################### != out |
72 | 1 | tkerber | self.xc = xc
|
73 | 1 | tkerber | self.kpts = kpts
|
74 | 1 | tkerber | self.nbands = nbands
|
75 | 1 | tkerber | self.width = width
|
76 | 1 | tkerber | self.meshcutoff = meshcutoff
|
77 | 1 | tkerber | self.charge = charge
|
78 | 1 | tkerber | self.pulay = pulay
|
79 | 1 | tkerber | self.mix = mix
|
80 | 1 | tkerber | self.maxiter = maxiter
|
81 | 1 | tkerber | self.basis = basis
|
82 | 1 | tkerber | self.ghosts = ghosts
|
83 | 1 | tkerber | self.write_fdf_file = write_fdf
|
84 | 1 | tkerber | |
85 | 1 | tkerber | self.converged = False |
86 | 1 | tkerber | self.fdf = {}
|
87 | 1 | tkerber | self.e_fermi = None |
88 | 1 | tkerber | |
89 | 1 | tkerber | def update(self, atoms): |
90 | 1 | tkerber | if (not self.converged or |
91 | 1 | tkerber | len(self.numbers) != len(atoms) or |
92 | 1 | tkerber | (self.numbers != atoms.get_atomic_numbers()).any()):
|
93 | 1 | tkerber | self.initialize(atoms)
|
94 | 1 | tkerber | self.calculate(atoms)
|
95 | 1 | tkerber | elif ((self.positions != atoms.get_positions()).any() or |
96 | 1 | tkerber | (self.pbc != atoms.get_pbc()).any() or |
97 | 1 | tkerber | (self.cell != atoms.get_cell()).any()):
|
98 | 1 | tkerber | self.calculate(atoms)
|
99 | 1 | tkerber | |
100 | 1 | tkerber | def initialize(self, atoms): |
101 | 1 | tkerber | self.numbers = atoms.get_atomic_numbers().copy()
|
102 | 1 | tkerber | self.species = []
|
103 | 1 | tkerber | for a, Z in enumerate(self.numbers): |
104 | 1 | tkerber | if a in self.ghosts: |
105 | 1 | tkerber | Z = -Z |
106 | 1 | tkerber | if Z not in self.species: |
107 | 1 | tkerber | self.species.append(Z)
|
108 | 1 | tkerber | |
109 | 1 | tkerber | if 'SIESTA_PP_PATH' in os.environ: |
110 | 1 | tkerber | pppaths = os.environ['SIESTA_PP_PATH'].split(':') |
111 | 1 | tkerber | else:
|
112 | 1 | tkerber | pppaths = [] |
113 | 1 | tkerber | |
114 | 1 | tkerber | for Z in self.species: |
115 | 1 | tkerber | symbol = chemical_symbols[abs(Z)]
|
116 | 1 | tkerber | name = symbol + '.vps'
|
117 | 1 | tkerber | name1 = symbol + '.psf'
|
118 | 1 | tkerber | found = False
|
119 | 1 | tkerber | for path in pppaths: |
120 | 1 | tkerber | filename = join(path, name) |
121 | 1 | tkerber | filename1 = join(path, name1) |
122 | 1 | tkerber | if isfile(filename) or islink(filename): |
123 | 1 | tkerber | found = True
|
124 | 1 | tkerber | if path != '.': |
125 | 1 | tkerber | if islink(name) or isfile(name): |
126 | 1 | tkerber | os.remove(name) |
127 | 1 | tkerber | os.symlink(filename, name) |
128 | 1 | tkerber | |
129 | 1 | tkerber | elif isfile(filename1) or islink(filename1): |
130 | 1 | tkerber | found = True
|
131 | 1 | tkerber | if path != '.': |
132 | 1 | tkerber | if islink(name1) or isfile(name1): |
133 | 1 | tkerber | os.remove(name1) |
134 | 1 | tkerber | os.symlink(filename1, name1) |
135 | 1 | tkerber | if not found: |
136 | 1 | tkerber | raise RuntimeError('No pseudopotential for %s!' % symbol) |
137 | 1 | tkerber | |
138 | 1 | tkerber | self.converged = False |
139 | 1 | tkerber | |
140 | 1 | tkerber | def get_potential_energy(self, atoms, force_consistent=False): |
141 | 1 | tkerber | self.update(atoms)
|
142 | 1 | tkerber | |
143 | 1 | tkerber | if force_consistent:
|
144 | 1 | tkerber | return self.efree |
145 | 1 | tkerber | else:
|
146 | 1 | tkerber | # Energy extrapolated to zero Kelvin:
|
147 | 1 | tkerber | return (self.etotal + self.efree) / 2 |
148 | 1 | tkerber | |
149 | 1 | tkerber | def get_forces(self, atoms): |
150 | 1 | tkerber | self.update(atoms)
|
151 | 1 | tkerber | return self.forces.copy() |
152 | 1 | tkerber | |
153 | 1 | tkerber | def get_stress(self, atoms): |
154 | 1 | tkerber | self.update(atoms)
|
155 | 1 | tkerber | return self.stress.copy() |
156 | 1 | tkerber | |
157 | 1 | tkerber | def get_dipole_moment(self, atoms): |
158 | 1 | tkerber | """Returns total dipole moment of the system."""
|
159 | 1 | tkerber | self.update(atoms)
|
160 | 1 | tkerber | return self.dipole |
161 | 1 | tkerber | |
162 | 1 | tkerber | def read_dipole(self): |
163 | 1 | tkerber | dipolemoment = np.zeros([1, 3]) |
164 | 1 | tkerber | for line in open(self.label + '.txt', 'r'): |
165 | 1 | tkerber | if line.rfind('Electric dipole (Debye)') > -1: |
166 | 1 | tkerber | dipolemoment = np.array([float(f) for f in line.split()[5:8]]) |
167 | 1 | tkerber | #debye to e*Ang (the units of VASP)
|
168 | 1 | tkerber | dipolemoment = dipolemoment*0.2081943482534
|
169 | 1 | tkerber | return dipolemoment
|
170 | 1 | tkerber | |
171 | 1 | tkerber | def calculate(self, atoms): |
172 | 1 | tkerber | self.positions = atoms.get_positions().copy()
|
173 | 1 | tkerber | self.cell = atoms.get_cell().copy()
|
174 | 1 | tkerber | self.pbc = atoms.get_pbc().copy()
|
175 | 1 | tkerber | |
176 | 1 | tkerber | if self.write_fdf_file: |
177 | 1 | tkerber | self.write_fdf(atoms)
|
178 | 1 | tkerber | |
179 | 1 | tkerber | siesta = os.environ['SIESTA_SCRIPT']
|
180 | 1 | tkerber | locals = {'label': self.label} |
181 | 1 | tkerber | execfile(siesta, {}, locals) |
182 | 1 | tkerber | exitcode = locals['exitcode'] |
183 | 1 | tkerber | if exitcode != 0: |
184 | 1 | tkerber | raise RuntimeError(('Siesta exited with exit code: %d. ' + |
185 | 1 | tkerber | 'Check %s.txt for more information.') %
|
186 | 1 | tkerber | (exitcode, self.label))
|
187 | 1 | tkerber | |
188 | 1 | tkerber | self.dipole = self.read_dipole() |
189 | 1 | tkerber | self.read()
|
190 | 1 | tkerber | |
191 | 1 | tkerber | self.converged = True |
192 | 1 | tkerber | |
193 | 1 | tkerber | def set_fdf(self, key, value): |
194 | 1 | tkerber | """Set FDF parameter."""
|
195 | 1 | tkerber | self.fdf[key] = value
|
196 | 1 | tkerber | |
197 | 1 | tkerber | def write_fdf(self, atoms): |
198 | 1 | tkerber | """Write input parameters to fdf-file."""
|
199 | 1 | tkerber | fh = open(self.label + '.fdf', 'w') |
200 | 1 | tkerber | |
201 | 1 | tkerber | fdf = { |
202 | 1 | tkerber | 'SystemLabel': self.label, |
203 | 1 | tkerber | 'AtomicCoordinatesFormat': 'Ang', |
204 | 1 | tkerber | 'LatticeConstant': 1.0, |
205 | 1 | tkerber | 'NumberOfAtoms': len(atoms), |
206 | 1 | tkerber | 'MeshCutoff': self.meshcutoff, |
207 | 1 | tkerber | 'NetCharge': self.charge, |
208 | 1 | tkerber | 'ElectronicTemperature': self.width, |
209 | 1 | tkerber | 'NumberOfEigenStates': self.nbands, |
210 | 1 | tkerber | 'DM.UseSaveDM': self.converged, |
211 | 1 | tkerber | 'PAO.BasisSize': self.basis, |
212 | 1 | tkerber | 'SolutionMethod': 'diagon', |
213 | 1 | tkerber | 'DM.NumberPulay': self.pulay, |
214 | 1 | tkerber | 'DM.MixingWeight': self.mix, |
215 | 1 | tkerber | 'MaxSCFIterations': self.maxiter |
216 | 1 | tkerber | } |
217 | 1 | tkerber | |
218 | 1 | tkerber | if self.xc != 'LDA': |
219 | 1 | tkerber | fdf['xc.functional'] = 'GGA' |
220 | 1 | tkerber | fdf['xc.authors'] = self.xc |
221 | 1 | tkerber | |
222 | 1 | tkerber | magmoms = atoms.get_initial_magnetic_moments() |
223 | 1 | tkerber | if magmoms.any():
|
224 | 1 | tkerber | fdf['SpinPolarized'] = True |
225 | 1 | tkerber | fh.write('%block InitSpin\n')
|
226 | 1 | tkerber | for n, M in enumerate(magmoms): |
227 | 1 | tkerber | if M != 0: |
228 | 1 | tkerber | fh.write('%d %.14f\n' % (n + 1, M)) |
229 | 1 | tkerber | fh.write('%endblock InitSpin\n')
|
230 | 1 | tkerber | |
231 | 1 | tkerber | fdf['Number_of_species'] = len(self.species) |
232 | 1 | tkerber | |
233 | 1 | tkerber | fdf.update(self.fdf)
|
234 | 1 | tkerber | |
235 | 1 | tkerber | for key, value in fdf.items(): |
236 | 1 | tkerber | if value is None: |
237 | 1 | tkerber | continue
|
238 | 1 | tkerber | |
239 | 1 | tkerber | if isinstance(value, list): |
240 | 1 | tkerber | fh.write('%%block %s\n' % key)
|
241 | 1 | tkerber | for line in value: |
242 | 1 | tkerber | fh.write(line + '\n')
|
243 | 1 | tkerber | fh.write('%%endblock %s\n' % key)
|
244 | 1 | tkerber | else:
|
245 | 1 | tkerber | unit = keys_with_units.get(fdfify(key)) |
246 | 1 | tkerber | if unit is None: |
247 | 1 | tkerber | fh.write('%s %s\n' % (key, value))
|
248 | 1 | tkerber | else:
|
249 | 1 | tkerber | if 'fs**2' in unit: |
250 | 1 | tkerber | value /= fs**2
|
251 | 1 | tkerber | elif 'fs' in unit: |
252 | 1 | tkerber | value /= fs |
253 | 1 | tkerber | fh.write('%s %f %s\n' % (key, value, unit))
|
254 | 1 | tkerber | |
255 | 1 | tkerber | fh.write('%block LatticeVectors\n')
|
256 | 1 | tkerber | for v in self.cell: |
257 | 1 | tkerber | fh.write('%.14f %.14f %.14f\n' % tuple(v)) |
258 | 1 | tkerber | fh.write('%endblock LatticeVectors\n')
|
259 | 1 | tkerber | |
260 | 1 | tkerber | fh.write('%block Chemical_Species_label\n')
|
261 | 1 | tkerber | for n, Z in enumerate(self.species): |
262 | 1 | tkerber | fh.write('%d %s %s\n' % (n + 1, Z, chemical_symbols[abs(Z)])) |
263 | 1 | tkerber | fh.write('%endblock Chemical_Species_label\n')
|
264 | 1 | tkerber | |
265 | 1 | tkerber | fh.write('%block AtomicCoordinatesAndAtomicSpecies\n')
|
266 | 1 | tkerber | a = 0
|
267 | 1 | tkerber | for pos, Z in zip(self.positions, self.numbers): |
268 | 1 | tkerber | if a in self.ghosts: |
269 | 1 | tkerber | Z = -Z |
270 | 1 | tkerber | a += 1
|
271 | 1 | tkerber | fh.write('%.14f %.14f %.14f' % tuple(pos)) |
272 | 1 | tkerber | fh.write(' %d\n' % (self.species.index(Z) + 1)) |
273 | 1 | tkerber | fh.write('%endblock AtomicCoordinatesAndAtomicSpecies\n')
|
274 | 1 | tkerber | |
275 | 1 | tkerber | if self.kpts is not None: |
276 | 1 | tkerber | fh.write('%block kgrid_Monkhorst_Pack\n')
|
277 | 1 | tkerber | for i in range(3): |
278 | 1 | tkerber | for j in range(3): |
279 | 1 | tkerber | if i == j:
|
280 | 1 | tkerber | fh.write('%d ' % self.kpts[i]) |
281 | 1 | tkerber | else:
|
282 | 1 | tkerber | fh.write('0 ')
|
283 | 1 | tkerber | fh.write('%.1f\n' % (((self.kpts[i] + 1) % 2) * 0.5)) |
284 | 1 | tkerber | fh.write('%endblock kgrid_Monkhorst_Pack\n')
|
285 | 1 | tkerber | |
286 | 1 | tkerber | fh.close() |
287 | 1 | tkerber | |
288 | 1 | tkerber | def read(self): |
289 | 1 | tkerber | """Read results from SIESTA's text-output file."""
|
290 | 1 | tkerber | text = open(self.label + '.txt', 'r').read().lower() |
291 | 1 | tkerber | assert 'error' not in text |
292 | 1 | tkerber | lines = iter(text.split('\n')) |
293 | 1 | tkerber | |
294 | 1 | tkerber | # Get the number of grid points used:
|
295 | 1 | tkerber | for line in lines: |
296 | 1 | tkerber | if line.startswith('initmesh: mesh ='): |
297 | 1 | tkerber | self.grid = [int(word) for word in line.split()[3:8:2]] |
298 | 1 | tkerber | break
|
299 | 1 | tkerber | |
300 | 1 | tkerber | # Stress (fixed so it's compatible with a MD run from siesta):
|
301 | 1 | tkerber | for line in lines: |
302 | 1 | tkerber | if line.startswith('siesta: stress tensor '): |
303 | 1 | tkerber | self.stress = np.empty((3, 3)) |
304 | 1 | tkerber | for i in range(3): |
305 | 1 | tkerber | tmp = lines.next().split() |
306 | 1 | tkerber | if len(tmp) == 4: |
307 | 1 | tkerber | self.stress[i] = [float(word) for word in tmp[1:]] |
308 | 1 | tkerber | else:
|
309 | 1 | tkerber | self.stress[i] = [float(word) for word in tmp] |
310 | 1 | tkerber | break
|
311 | 1 | tkerber | else:
|
312 | 1 | tkerber | raise RuntimeError |
313 | 1 | tkerber | |
314 | 1 | tkerber | text = open(self.label + '.txt', 'r').read().lower() |
315 | 1 | tkerber | lines = iter(text.split('\n')) |
316 | 1 | tkerber | # Energy (again a fix to make it compatible with a MD run from siesta):
|
317 | 1 | tkerber | counter = 0
|
318 | 1 | tkerber | for line in lines: |
319 | 1 | tkerber | if line.startswith('siesta: etot =') and counter == 0: |
320 | 1 | tkerber | counter += 1
|
321 | 1 | tkerber | elif line.startswith('siesta: etot ='): |
322 | 1 | tkerber | self.etotal = float(line.split()[-1]) |
323 | 1 | tkerber | self.efree = float(lines.next().split()[-1]) |
324 | 1 | tkerber | break
|
325 | 1 | tkerber | else:
|
326 | 1 | tkerber | raise RuntimeError |
327 | 1 | tkerber | |
328 | 1 | tkerber | # Forces (changed so forces smaller than -999eV/A can be fetched):
|
329 | 1 | tkerber | lines = open(self.label + '.FA', 'r').readlines() |
330 | 1 | tkerber | assert int(lines[0]) == len(self.numbers) |
331 | 1 | tkerber | assert len(lines) == len(self.numbers) + 1 |
332 | 1 | tkerber | lines = lines[1:]
|
333 | 1 | tkerber | self.forces = np.zeros((len(lines), 3)) |
334 | 1 | tkerber | for i in range(len(lines)): |
335 | 1 | tkerber | self.forces[i, 0] = float(lines[i][6:18].strip()) |
336 | 1 | tkerber | self.forces[i, 1] = float(lines[i][18:30].strip()) |
337 | 1 | tkerber | self.forces[i, 2] = float(lines[i][30:42].strip()) |
338 | 1 | tkerber | |
339 | 1 | tkerber | def read_eig(self): |
340 | 1 | tkerber | if self.e_fermi is not None: |
341 | 1 | tkerber | return
|
342 | 1 | tkerber | |
343 | 1 | tkerber | assert os.access(self.label + '.EIG', os.F_OK) |
344 | 1 | tkerber | assert os.access(self.label + '.KP', os.F_OK) |
345 | 1 | tkerber | |
346 | 1 | tkerber | # Read k point weights
|
347 | 1 | tkerber | text = open(self.label + '.KP', 'r').read() |
348 | 1 | tkerber | lines = text.split('\n')
|
349 | 1 | tkerber | n_kpts = int(lines[0].strip()) |
350 | 1 | tkerber | self.weights = np.zeros((n_kpts,))
|
351 | 1 | tkerber | for i in range(n_kpts): |
352 | 1 | tkerber | l = lines[i + 1].split()
|
353 | 1 | tkerber | self.weights[i] = float(l[4]) |
354 | 1 | tkerber | |
355 | 1 | tkerber | # Read eigenvalues and fermi-level
|
356 | 1 | tkerber | text = open(self.label+'.EIG','r').read() |
357 | 1 | tkerber | lines = text.split('\n')
|
358 | 1 | tkerber | self.e_fermi = float(lines[0].split()[0]) |
359 | 1 | tkerber | tmp = lines[1].split()
|
360 | 1 | tkerber | self.n_bands = int(tmp[0]) |
361 | 1 | tkerber | n_spin_bands = int(tmp[1]) |
362 | 1 | tkerber | self.spin_pol = n_spin_bands == 2 |
363 | 1 | tkerber | lines = lines[2:-1] |
364 | 1 | tkerber | lines_per_kpt = (self.n_bands * n_spin_bands / 10 + |
365 | 1 | tkerber | int((self.n_bands * n_spin_bands) % 10 != 0)) |
366 | 1 | tkerber | self.eig = dict() |
367 | 1 | tkerber | for i in range(len(self.weights)): |
368 | 1 | tkerber | tmp = lines[i * lines_per_kpt:(i + 1) * lines_per_kpt]
|
369 | 1 | tkerber | v = [float(v) for v in tmp[0].split()[1:]] |
370 | 1 | tkerber | for l in tmp[1:]: |
371 | 1 | tkerber | v.extend([float(t) for t in l.split()]) |
372 | 1 | tkerber | if self.spin_pol: |
373 | 1 | tkerber | self.eig[(i, 1)] = np.array(v[0:self.n_bands]) |
374 | 1 | tkerber | self.eig[(i, -1)] = np.array(v[self.n_bands:]) |
375 | 1 | tkerber | else:
|
376 | 1 | tkerber | self.eig[(i, 1)] = np.array(v) |
377 | 1 | tkerber | |
378 | 1 | tkerber | def get_k_point_weights(self): |
379 | 1 | tkerber | self.read_eig()
|
380 | 1 | tkerber | return self.weights |
381 | 1 | tkerber | |
382 | 1 | tkerber | def get_fermi_level(self): |
383 | 1 | tkerber | self.read_eig()
|
384 | 1 | tkerber | return self.e_fermi |
385 | 1 | tkerber | |
386 | 1 | tkerber | def get_eigenvalues(self, kpt=0, spin=1): |
387 | 1 | tkerber | self.read_eig()
|
388 | 1 | tkerber | return self.eig[(kpt, spin)] |
389 | 1 | tkerber | |
390 | 1 | tkerber | def get_number_of_spins(self): |
391 | 1 | tkerber | self.read_eig()
|
392 | 1 | tkerber | if self.spin_pol: |
393 | 1 | tkerber | return 2 |
394 | 1 | tkerber | else:
|
395 | 1 | tkerber | return 1 |
396 | 1 | tkerber | |
397 | 1 | tkerber | def read_hs(self, filename, is_gamma_only=False, magnus=False): |
398 | 1 | tkerber | """Read the Hamiltonian and overlap matrix from a Siesta
|
399 | 1 | tkerber | calculation in sparse format.
|
400 | 1 | tkerber |
|
401 | 1 | tkerber | Parameters
|
402 | 1 | tkerber | ==========
|
403 | 1 | tkerber | filename: str
|
404 | 1 | tkerber | The filename should be on the form jobname.HS
|
405 | 1 | tkerber | is_gamma_only: {False, True), optional
|
406 | 1 | tkerber | Is it a gamma point calculation?
|
407 | 1 | tkerber | magnus: bool
|
408 | 1 | tkerber | The fileformat was changed by Magnus in Siesta at some
|
409 | 1 | tkerber | point around version 2.xxx.
|
410 | 1 | tkerber | Use mangus=False, to use the old file format.
|
411 | 1 | tkerber |
|
412 | 1 | tkerber | Note
|
413 | 1 | tkerber | ====
|
414 | 1 | tkerber | Data read in is put in self._dat.
|
415 | 1 | tkerber |
|
416 | 1 | tkerber | Examples
|
417 | 1 | tkerber | ========
|
418 | 1 | tkerber | >>> calc = Siesta()
|
419 | 1 | tkerber | >>> calc.read_hs('jobname.HS')
|
420 | 1 | tkerber | >>> print calc._dat.fermi_level
|
421 | 1 | tkerber | >>> print 'Number of orbitals: %i' % calc._dat.nuotot
|
422 | 1 | tkerber | """
|
423 | 1 | tkerber | assert not magnus, 'Not implemented; changes by Magnus to file io' |
424 | 1 | tkerber | assert not is_gamma_only, 'Not implemented. Only works for k-points.' |
425 | 1 | tkerber | class Dummy: |
426 | 1 | tkerber | pass
|
427 | 1 | tkerber | self._dat = dat = Dummy()
|
428 | 1 | tkerber | # Try to read supercell and atom data from a jobname.XV file
|
429 | 1 | tkerber | filename_xv = filename[:-2] + 'XV' |
430 | 1 | tkerber | #assert isfile(filename_xv), 'Missing jobname.XV file'
|
431 | 1 | tkerber | if isfile(filename_xv):
|
432 | 1 | tkerber | print 'Reading supercell and atom data from ' + filename_xv |
433 | 1 | tkerber | fd = open(filename_xv, 'r') |
434 | 1 | tkerber | dat.cell = np.zeros((3, 3)) # Supercell |
435 | 1 | tkerber | for a_vec in dat.cell: |
436 | 1 | tkerber | a_vec[:] = np.array(fd.readline().split()[:3], float) |
437 | 1 | tkerber | dat.rcell = 2 * np.pi * np.linalg.inv(dat.cell.T)
|
438 | 1 | tkerber | dat.natoms = int(fd.readline().split()[0]) |
439 | 1 | tkerber | dat.symbols = [] |
440 | 1 | tkerber | dat.pos_ac = np.zeros((dat.natoms, 3))
|
441 | 1 | tkerber | for a in range(dat.natoms): |
442 | 1 | tkerber | line = fd.readline().split() |
443 | 1 | tkerber | dat.symbols.append(chemical_symbols[int(line[1])]) |
444 | 1 | tkerber | dat.pos_ac[a, :] = [float(line[i]) for i in range(2, 2 + 3)] |
445 | 1 | tkerber | # Read in the jobname.HS file
|
446 | 1 | tkerber | fileobj = file(filename, 'rb') |
447 | 1 | tkerber | fileobj.seek(0)
|
448 | 1 | tkerber | dat.fermi_level = float(open(filename[:-3] + '.EIG', 'r').readline()) |
449 | 1 | tkerber | dat.is_gammay_only = is_gamma_only |
450 | 1 | tkerber | dat.nuotot, dat.ns, dat.mnh = getrecord(fileobj, 'l')
|
451 | 1 | tkerber | nuotot, ns, mnh = dat.nuotot, dat.ns, dat.mnh |
452 | 1 | tkerber | print 'Number of orbitals found: %i' % nuotot |
453 | 1 | tkerber | dat.numh = numh = np.array([getrecord(fileobj, 'l')
|
454 | 1 | tkerber | for i in range(nuotot)], 'l') |
455 | 1 | tkerber | dat.maxval = max(numh)
|
456 | 1 | tkerber | dat.listhptr = listhptr = np.zeros(nuotot, 'l')
|
457 | 1 | tkerber | listhptr[0] = 0 |
458 | 1 | tkerber | for oi in xrange(1, nuotot): |
459 | 1 | tkerber | listhptr[oi] = listhptr[oi - 1] + numh[oi - 1] |
460 | 1 | tkerber | dat.listh = listh = np.zeros(mnh, 'l')
|
461 | 1 | tkerber | |
462 | 1 | tkerber | print 'Reading sparse info' |
463 | 1 | tkerber | for oi in xrange(nuotot): |
464 | 1 | tkerber | for mi in xrange(numh[oi]): |
465 | 1 | tkerber | listh[listhptr[oi] + mi] = getrecord(fileobj, 'l')
|
466 | 1 | tkerber | |
467 | 1 | tkerber | dat.nuotot_sc = max(listh)
|
468 | 1 | tkerber | dat.h_sparse = h_sparse = np.zeros((mnh, ns), float)
|
469 | 1 | tkerber | dat.s_sparse = s_sparse = np.zeros(mnh, float)
|
470 | 1 | tkerber | print 'Reading H' |
471 | 1 | tkerber | for si in xrange(ns): |
472 | 1 | tkerber | for oi in xrange(nuotot): |
473 | 1 | tkerber | for mi in xrange(numh[oi]): |
474 | 1 | tkerber | h_sparse[listhptr[oi] + mi, si] = getrecord(fileobj, 'd')
|
475 | 1 | tkerber | print 'Reading S' |
476 | 1 | tkerber | for oi in xrange(nuotot): |
477 | 1 | tkerber | for mi in xrange(numh[oi]): |
478 | 1 | tkerber | s_sparse[listhptr[oi] + mi] = getrecord(fileobj, 'd')
|
479 | 1 | tkerber | |
480 | 1 | tkerber | dat.qtot, dat.temperature = getrecord(fileobj, 'd')
|
481 | 1 | tkerber | if not is_gamma_only: |
482 | 1 | tkerber | print 'Reading X' |
483 | 1 | tkerber | dat.xij_sparse = xij_sparse = np.zeros([3, mnh], float) |
484 | 1 | tkerber | for oi in xrange(nuotot): |
485 | 1 | tkerber | for mi in xrange(numh[oi]): |
486 | 1 | tkerber | xij_sparse[:, listhptr[oi] + mi] = getrecord(fileobj, 'd')
|
487 | 1 | tkerber | fileobj.close() |
488 | 1 | tkerber | |
489 | 1 | tkerber | def get_hs(self, kpt=(0, 0, 0), spin=0, remove_pbc=None, kpt_scaled=True): |
490 | 1 | tkerber | """Hamiltonian and overlap matrices for an arbitrary k-point.
|
491 | 1 | tkerber |
|
492 | 1 | tkerber | The default values corresponds to the Gamma point for
|
493 | 1 | tkerber | spin 0 and periodic boundary conditions.
|
494 | 1 | tkerber |
|
495 | 1 | tkerber | Parameters
|
496 | 1 | tkerber | ==========
|
497 | 1 | tkerber | kpt : {(0, 0, 0), (3,) array_like}, optional
|
498 | 1 | tkerber | k-point in scaled or absolute coordinates.
|
499 | 1 | tkerber | For the latter the units should be Bohr^-1.
|
500 | 1 | tkerber | spin : {0, 1}, optional
|
501 | 1 | tkerber | Spin index
|
502 | 1 | tkerber | remove_pbc : {None, ({'x', 'y', 'z'}, basis)}, optional
|
503 | 1 | tkerber | Use remove_pbc to truncate h and s along a cartesian
|
504 | 1 | tkerber | axis.
|
505 | 1 | tkerber | basis: {str, dict}
|
506 | 1 | tkerber | The basis specification as either a string or a dictionary.
|
507 | 1 | tkerber | kpt_scaled : {True, bool}, optional
|
508 | 1 | tkerber | Use kpt_scaled=False if `kpt` is in absolute units (Bohr^-1).
|
509 | 1 | tkerber |
|
510 | 1 | tkerber | Note
|
511 | 1 | tkerber | ====
|
512 | 1 | tkerber | read_hs should be called before get_hs gets called.
|
513 | 1 | tkerber |
|
514 | 1 | tkerber | Examples
|
515 | 1 | tkerber | ========
|
516 | 1 | tkerber | >>> calc = Siesta()
|
517 | 1 | tkerber | >>> calc.read_hs('jobname.HS')
|
518 | 1 | tkerber | >>> h, s = calc.get_hs((0.0, 0.375, 0.375))
|
519 | 1 | tkerber | >>> h -= s * calc._dat.fermi_level # fermi level is now at 0.0
|
520 | 1 | tkerber | >>> basis = 'szp'
|
521 | 1 | tkerber | >>> h, s = calc.get_hs((0.0, 0.375, 0.375), remove_pbc=('x', basis))
|
522 | 1 | tkerber | >>> basis = {'Au:'sz}', 'C':'dzp', None:'szp'}
|
523 | 1 | tkerber | >>> h, s = calc.get_hs((0.0, 0.375, 0.375), remove_pbc=('x', basis))
|
524 | 1 | tkerber |
|
525 | 1 | tkerber | """
|
526 | 1 | tkerber | if not hasattr(self, '_dat'):# XXX Crude check if data is avail. |
527 | 1 | tkerber | print 'Please read in data first by calling the method read_hs.' |
528 | 1 | tkerber | return None, None |
529 | 1 | tkerber | dot = np.dot |
530 | 1 | tkerber | dat = self._dat
|
531 | 1 | tkerber | kpt_c = np.array(kpt, float)
|
532 | 1 | tkerber | if kpt_scaled:
|
533 | 1 | tkerber | kpt_c = dot(kpt_c, dat.rcell) |
534 | 1 | tkerber | |
535 | 1 | tkerber | h_MM = np.zeros((dat.nuotot, dat.nuotot), complex)
|
536 | 1 | tkerber | s_MM = np.zeros((dat.nuotot, dat.nuotot), complex)
|
537 | 1 | tkerber | h_sparse, s_sparse = dat.h_sparse, dat.s_sparse |
538 | 1 | tkerber | x_sparse = dat.xij_sparse |
539 | 1 | tkerber | numh, listhptr, listh = dat.numh, dat.listhptr, dat.listh |
540 | 1 | tkerber | indxuo = np.mod(np.arange(dat.nuotot_sc), dat.nuotot) |
541 | 1 | tkerber | |
542 | 1 | tkerber | for iuo in xrange(dat.nuotot): |
543 | 1 | tkerber | for j in range(numh[iuo]): |
544 | 1 | tkerber | ind = listhptr[iuo] + j |
545 | 1 | tkerber | jo = listh[ind] - 1
|
546 | 1 | tkerber | juo = indxuo[jo] |
547 | 1 | tkerber | kx = dot(kpt_c, x_sparse[:, ind]) |
548 | 1 | tkerber | phasef = exp(1.0j * kx)
|
549 | 1 | tkerber | h_MM[iuo, juo] += phasef * h_sparse[ind, spin] |
550 | 1 | tkerber | s_MM[iuo, juo] += phasef * s_sparse[ind] |
551 | 1 | tkerber | |
552 | 1 | tkerber | if remove_pbc is not None: |
553 | 1 | tkerber | direction, basis = remove_pbc |
554 | 1 | tkerber | centers_ic = get_bf_centers(dat.symbols, dat.pos_ac, basis) |
555 | 1 | tkerber | d = 'xyz'.index(direction)
|
556 | 1 | tkerber | cutoff = dat.cell[d, d] * 0.5
|
557 | 1 | tkerber | truncate_along_axis(h_MM, s_MM, direction, centers_ic, cutoff) |
558 | 1 | tkerber | |
559 | 1 | tkerber | h_MM *= complex(Rydberg)
|
560 | 1 | tkerber | return h_MM, s_MM
|
561 | 1 | tkerber | |
562 | 1 | tkerber | |
563 | 1 | tkerber | def getrecord(fileobj, dtype): |
564 | 1 | tkerber | """Used to read in binary files.
|
565 | 1 | tkerber | """
|
566 | 1 | tkerber | typetosize = {'l':4, 'f':4, 'd':8}# XXX np.int, np.float32, np.float64 |
567 | 1 | tkerber | assert dtype in typetosize # XXX |
568 | 1 | tkerber | size = typetosize[dtype] |
569 | 1 | tkerber | record = array.array('l')
|
570 | 1 | tkerber | trunk = array.array(dtype) |
571 | 1 | tkerber | record.fromfile(fileobj, 1)
|
572 | 1 | tkerber | nofelements = int(record[-1]) / size |
573 | 1 | tkerber | trunk.fromfile(fileobj, nofelements) |
574 | 1 | tkerber | record.fromfile(fileobj, 1)
|
575 | 1 | tkerber | data = np.array(trunk, dtype=dtype) |
576 | 1 | tkerber | if len(data)==1: |
577 | 1 | tkerber | data = data[0]
|
578 | 1 | tkerber | return data
|
579 | 1 | tkerber | |
580 | 1 | tkerber | def truncate_along_axis(h, s, direction, centers_ic, cutoff): |
581 | 1 | tkerber | """Truncate h and s such along a cartesian axis.
|
582 | 1 | tkerber |
|
583 | 1 | tkerber | Parameters:
|
584 | 1 | tkerber |
|
585 | 1 | tkerber | h: (N, N) ndarray
|
586 | 1 | tkerber | Hamiltonian matrix.
|
587 | 1 | tkerber | s: (N, N) ndarray
|
588 | 1 | tkerber | Overlap matrix.
|
589 | 1 | tkerber | direction: {'x', 'y', 'z'}
|
590 | 1 | tkerber | Truncate allong a cartesian axis.
|
591 | 1 | tkerber | centers_ic: (N, 3) ndarray
|
592 | 1 | tkerber | Centers of the basis functions.
|
593 | 1 | tkerber | cutoff: float
|
594 | 1 | tkerber | The (direction-axis projected) cutoff distance.
|
595 | 1 | tkerber | """
|
596 | 1 | tkerber | dtype = h.dtype |
597 | 1 | tkerber | ni = len(centers_ic)
|
598 | 1 | tkerber | d = 'xyz'.index(direction)
|
599 | 1 | tkerber | pos_i = centers_ic[:, d] |
600 | 1 | tkerber | for i in range(ni): |
601 | 1 | tkerber | dpos_i = abs(pos_i - pos_i[i])
|
602 | 1 | tkerber | mask_i = (dpos_i < cutoff).astype(dtype) |
603 | 1 | tkerber | h[i, :] *= mask_i |
604 | 1 | tkerber | h[:, i] *= mask_i |
605 | 1 | tkerber | s[i, :] *= mask_i |
606 | 1 | tkerber | s[:, i] *= mask_i |
607 | 1 | tkerber | |
608 | 1 | tkerber | def get_nao(symbol, basis): |
609 | 1 | tkerber | """Number of basis functions.
|
610 | 1 | tkerber |
|
611 | 1 | tkerber | Parameters
|
612 | 1 | tkerber | ==========
|
613 | 1 | tkerber | symbol: str
|
614 | 1 | tkerber | The chemical symbol.
|
615 | 1 | tkerber | basis: str
|
616 | 1 | tkerber | Basis function type.
|
617 | 1 | tkerber | """
|
618 | 1 | tkerber | ls = valence_config[symbol] |
619 | 1 | tkerber | nao = 0
|
620 | 1 | tkerber | zeta = {'s':1, 'd':2, 't':3, 'q':4} |
621 | 1 | tkerber | nzeta = zeta[basis[0]]
|
622 | 1 | tkerber | is_pol = 'p' in basis |
623 | 1 | tkerber | for l in ls: |
624 | 1 | tkerber | nao += (2 * l + 1) * nzeta |
625 | 1 | tkerber | if is_pol:
|
626 | 1 | tkerber | l_pol = None
|
627 | 1 | tkerber | l = -1
|
628 | 1 | tkerber | while l_pol is None: |
629 | 1 | tkerber | l += 1
|
630 | 1 | tkerber | if not l in ls: |
631 | 1 | tkerber | l_pol = l |
632 | 1 | tkerber | nao += 2 * l_pol + 1 |
633 | 1 | tkerber | return nao
|
634 | 1 | tkerber | |
635 | 1 | tkerber | def get_bf_centers(symbols, positions, basis): |
636 | 1 | tkerber | """Centers of basis functions.
|
637 | 1 | tkerber |
|
638 | 1 | tkerber | Parameters
|
639 | 1 | tkerber | ==========
|
640 | 1 | tkerber | symbols: str, (N, ) array_like
|
641 | 1 | tkerber | chemical symbol for each atom.
|
642 | 1 | tkerber | positions: float, (N, 3) array_like
|
643 | 1 | tkerber | Positions of the atoms.
|
644 | 1 | tkerber | basis: {str, dict}
|
645 | 1 | tkerber | Basis set specification as either a string or a dictionary
|
646 | 1 | tkerber |
|
647 | 1 | tkerber | Examples
|
648 | 1 | tkerber | ========
|
649 | 1 | tkerber | >>> symbols = ['O', 'H']
|
650 | 1 | tkerber | >>> positions = [(0, 0, 0), (0, 0, 1)]
|
651 | 1 | tkerber | >>> basis = 'sz'
|
652 | 1 | tkerber | >>> print get_bf_centers(symbols, positions, basis)
|
653 | 1 | tkerber | [[0 0 0]
|
654 | 1 | tkerber | [0 0 0]
|
655 | 1 | tkerber | [0 0 0]
|
656 | 1 | tkerber | [0 0 0]
|
657 | 1 | tkerber | [0 0 1]]
|
658 | 1 | tkerber | >>> basis = {'H':'dz', None:'sz'}
|
659 | 1 | tkerber | >>> print get_bf_centers(symbols, positions, basis)
|
660 | 1 | tkerber | [[0 0 0]
|
661 | 1 | tkerber | [0 0 0]
|
662 | 1 | tkerber | [0 0 0]
|
663 | 1 | tkerber | [0 0 0]
|
664 | 1 | tkerber | [0 0 1]
|
665 | 1 | tkerber | [0 0 1]]
|
666 | 1 | tkerber |
|
667 | 1 | tkerber | """
|
668 | 1 | tkerber | centers_ic = [] |
669 | 1 | tkerber | dict_basis = False
|
670 | 1 | tkerber | if type(basis)==dict: |
671 | 1 | tkerber | dict_basis = True
|
672 | 1 | tkerber | for symbol, pos in zip(symbols, positions): |
673 | 1 | tkerber | if dict_basis:
|
674 | 1 | tkerber | if symbol not in basis: |
675 | 1 | tkerber | bas = basis[None]
|
676 | 1 | tkerber | else:
|
677 | 1 | tkerber | bas = basis[symbol] |
678 | 1 | tkerber | else:
|
679 | 1 | tkerber | bas = basis |
680 | 1 | tkerber | for i in range(get_nao(symbol, bas)): |
681 | 1 | tkerber | centers_ic.append(pos) |
682 | 1 | tkerber | return np.asarray(centers_ic)
|
683 | 1 | tkerber | |
684 | 1 | tkerber | def fdfify(key): |
685 | 1 | tkerber | return key.lower().replace('_', '').replace('.', '').replace('-', '') |
686 | 1 | tkerber | |
687 | 1 | tkerber | valence_config = { |
688 | 1 | tkerber | 'H': (0,), |
689 | 1 | tkerber | 'C': (0, 1), |
690 | 1 | tkerber | 'N': (0, 1), |
691 | 1 | tkerber | 'O': (0, 1), |
692 | 1 | tkerber | 'S': (0, 1), |
693 | 1 | tkerber | 'Li': (0,), |
694 | 1 | tkerber | 'Na': (0,), |
695 | 1 | tkerber | 'Ni': (0, 2), |
696 | 1 | tkerber | 'Cu': (0, 2), |
697 | 1 | tkerber | 'Pd': (0, 2), |
698 | 1 | tkerber | 'Ag': (0, 2), |
699 | 1 | tkerber | 'Pt': (0, 2), |
700 | 1 | tkerber | 'Au': (0, 2)} |
701 | 1 | tkerber | |
702 | 1 | tkerber | keys_with_units = { |
703 | 1 | tkerber | 'paoenergyshift': 'eV', |
704 | 1 | tkerber | 'zmunitslength': 'Bohr', |
705 | 1 | tkerber | 'zmunitsangle': 'rad', |
706 | 1 | tkerber | 'zmforcetollength': 'eV/Ang', |
707 | 1 | tkerber | 'zmforcetolangle': 'eV/rad', |
708 | 1 | tkerber | 'zmmaxdispllength': 'Ang', |
709 | 1 | tkerber | 'zmmaxdisplangle': 'rad', |
710 | 1 | tkerber | 'meshcutoff': 'eV', |
711 | 1 | tkerber | 'dmenergytolerance': 'eV', |
712 | 1 | tkerber | 'electronictemperature': 'eV', |
713 | 1 | tkerber | 'oneta': 'eV', |
714 | 1 | tkerber | 'onetaalpha': 'eV', |
715 | 1 | tkerber | 'onetabeta': 'eV', |
716 | 1 | tkerber | 'onrclwf': 'Ang', |
717 | 1 | tkerber | 'onchemicalpotentialrc': 'Ang', |
718 | 1 | tkerber | 'onchemicalpotentialtemperature': 'eV', |
719 | 1 | tkerber | 'mdmaxcgdispl': 'Ang', |
720 | 1 | tkerber | 'mdmaxforcetol': 'eV/Ang', |
721 | 1 | tkerber | 'mdmaxstresstol': 'eV/Ang**3', |
722 | 1 | tkerber | 'mdlengthtimestep': 'fs', |
723 | 1 | tkerber | 'mdinitialtemperature': 'eV', |
724 | 1 | tkerber | 'mdtargettemperature': 'eV', |
725 | 1 | tkerber | 'mdtargetpressure': 'eV/Ang**3', |
726 | 1 | tkerber | 'mdnosemass': 'eV*fs**2', |
727 | 1 | tkerber | 'mdparrinellorahmanmass': 'eV*fs**2', |
728 | 1 | tkerber | 'mdtaurelax': 'fs', |
729 | 1 | tkerber | 'mdbulkmodulus': 'eV/Ang**3', |
730 | 1 | tkerber | 'mdfcdispl': 'Ang', |
731 | 1 | tkerber | 'warningminimumatomicdistance': 'Ang', |
732 | 1 | tkerber | 'rcspatial': 'Ang', |
733 | 1 | tkerber | 'kgridcutoff': 'Ang', |
734 | 1 | tkerber | 'latticeconstant': 'Ang'} |