Statistiques
| Révision :

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'}