ase.calculators.siesta — ASE documentation

### Navigation

- index
- modules |
- Module code »
- ase.calculators »

# Source code for ase.calculators.siesta

    from __future__ import print_function
    """This module defines an ASE interface to SIESTA.

    http://www.uam.es/departamentos/ciencias/fismateriac/siesta
    """

    import os
    from os.path import join, isfile, islink, getmtime
    from cmath import exp
    import array

    import numpy as np

    from ase.data import chemical_symbols
    from ase.units import Rydberg, fs
    from ase.io.siesta import read_rho, read_fdf, read_struct
    from ase.io.cube import read_cube_data

    class Siesta:
        """Class for doing SIESTA calculations.

        The default parameters are very close to those that the SIESTA
        Fortran code would use.  These are the exceptions::

          calc = Siesta(label='siesta', xc='LDA', pulay=5, mix=0.1)

        Use the set_fdf method to set extra FDF parameters::

          calc.set_fdf('PAO.EnergyShift', 0.01 * Rydberg)

        """
        def __init__(self, label='siesta', xc='LDA', kpts=None, nbands=None,
                     width=None, meshcutoff=None, charge=None,
                     pulay=5, mix=0.1, maxiter=120,
                     basis=None, ghosts=[],
                     write_fdf=True):
            """Construct SIESTA-calculator object.

            Parameters
            ==========
            label: str
                Prefix to use for filenames (label.fdf, label.txt, ...).
                Default is 'siesta'.
            xc: str
                Exchange-correlation functional.  Must be one of LDA, PBE,
                revPBE, RPBE.
            kpts: list of three int
                Monkhost-Pack sampling.
            nbands: int
                Number of bands.
            width: float
                Fermi-distribution width in eV.
            meshcutoff: float
                Cutoff energy in eV for grid.
            charge: float
                Total charge of the system.
            pulay: int
                Number of old densities to use for Pulay mixing.
            mix: float
                Mixing parameter between zero and one for density mixing.
            write_fdf: bool
                Use write_fdf=False to use your own fdf-file.

            Examples
            ========
            Use default values:

            >>> h = Atoms('H', calculator=Siesta())
            >>> h.center(vacuum=3.0)
            >>> e = h.get_potential_energy()

            """

            self.name = 'Siesta'
            self.label = label#################### != out
            self.xc = xc
            self.kpts = kpts
            self.nbands = nbands
            self.width = width
            self.meshcutoff = meshcutoff
            self.charge = charge
            self.pulay = pulay
            self.mix = mix
            self.maxiter = maxiter
            self.basis = basis
            self.ghosts = ghosts
            self.write_fdf_file = write_fdf

            self.converged = False
            self.fdf = {}
            self.e_fermi = None

        def update(self, atoms):
            if (not self.converged or
                len(self.numbers) != len(atoms) or
                (self.numbers != atoms.get_atomic_numbers()).any()):
                self.initialize(atoms)
                self.calculate(atoms)
            elif ((self.positions != atoms.get_positions()).any() or
                  (self.pbc != atoms.get_pbc()).any() or
                  (self.cell != atoms.get_cell()).any()):
                self.calculate(atoms)

        def initialize(self, atoms):
            self.numbers = atoms.get_atomic_numbers().copy()
            self.species = []
            for a, Z in enumerate(self.numbers):
                if a in self.ghosts:
                    Z = -Z
                if Z not in self.species:
                    self.species.append(Z)

            if 'SIESTA_PP_PATH' in os.environ:
                pppaths = os.environ['SIESTA_PP_PATH'].split(':')
            else:
                pppaths = []

            for Z in self.species:
                symbol = chemical_symbols[abs(Z)]
                name = symbol + '.vps'
                name1 = symbol + '.psf'
                found = False
                for path in pppaths:
                    filename = join(path, name)
                    filename1 = join(path, name1)
                    if isfile(filename) or islink(filename):
                        found = True
                        if path != '.':
                            if islink(name) or isfile(name):
                                os.remove(name)
                            os.symlink(filename, name)

                    elif isfile(filename1) or islink(filename1):
                        found = True
                        if path != '.':
                            if islink(name1) or isfile(name1):
                                os.remove(name1)
                            os.symlink(filename1, name1)
                if not found:
                    raise RuntimeError('No pseudopotential for %s!' % symbol)

            self.converged = False

        def get_potential_energy(self, atoms, force_consistent=False):
            self.update(atoms)

            if force_consistent:
                return self.efree
            else:
                # Energy extrapolated to zero Kelvin:
                return  (self.etotal + self.efree) / 2

        def get_forces(self, atoms):
            self.update(atoms)
            return self.forces.copy()

        def get_stress(self, atoms):
            self.update(atoms)
            return self.stress.copy()

        def get_dipole_moment(self, atoms):
            """Returns total dipole moment of the system."""
            self.update(atoms)
            return self.dipole

        def read_dipole(self):
            dipolemoment = np.zeros([1, 3])
            for line in open(self.label + '.txt', 'r'):
                if line.rfind('Electric dipole (Debye)') > -1:
                    dipolemoment = np.array([float(f) for f in line.split()[5:8]])
            #debye to e*Ang (the units of VASP)
            dipolemoment = dipolemoment*0.2081943482534
            return dipolemoment

        def get_pseudo_density(self, spin=None, pad=True):
            """Return pseudo-density array.

            If *spin* is not given, then the total density is returned.
            Otherwise, the spin up or down density is returned (spin=0 or 1).
            """
            filename = self.label + '.RHO'
            if not isfile(filename):
                raise RuntimeError('Could not find rho-file (make sure to add fdf-option '
                                   '"SaveRho=True" to your calculation)')

            rho = read_rho(filename)

            if spin is None:
                return rho.sum(axis=3)
            elif rho.shape[3] != 2:
                raise RuntimeError('Explicit spin-value requested. '
                                   'Only total density is available.')
            elif spin == 0 or spin == 1:
                return rho[:, :, :, spin]
            else:
                raise RuntimeError('Invalid spin-value requested. '
                                   'Expected 0 or 1, got %s' % spin)

        def get_pseudo_wave_function(self, band=0, kpt=0, spin=None):
            """Return pseudo-wave-function array.

            The method is limited to the gamma point, and is implemented
            as a wrapper to denchar (a tool shipped with siesta);
            denchar must be available in the command path.

            When retrieving a p_w_f from a non-spin-polarized calculation,
            spin must be None (default), and for spin-polarized
            calculations, spin must be set to either 0 (up) or 1 (down).

            As long as the necessary files are present and named
            correctly, old p_w_fs can be read as long as the
            calculator label is set. E.g.

            >>> c = Siesta(label='name_of_old_calculation')
            >>> pwf = c.get_pseudo_wave_function()

            The broadcast and pad options are not implemented.
            """

            # Not implemented: kpt=0, broadcast=True, pad=True
            # kpoint must be Gamma
            assert kpt == 0, \
                "siesta.get_pseudo_wave_function is unfortunately limited " \
                "to the gamma point only. kpt must be 0."

            # In denchar, band numbering starts from 1
            assert isinstance(band, int) and band >= 0
            band = band+1

            if spin is None:
                spin_name = ""
            elif spin == 0:
                spin_name = ".UP"
            elif spin == 1:
                spin_name = ".DOWN"

            label = self.label
            # If <label>.WF<band>.cube already exist and is newer than <label>.fdf,
            # just return it
            fn_wf = label+('.WF%i%s.cube'%(band,spin_name))
            fn_fdf = label+'.fdf'
            if isfile(fn_wf) and isfile(fn_fdf) and (getmtime(fn_wf) > getmtime(fn_fdf)):
                x, _ = read_cube_data(fn_wf)
                return x

            if not isfile(fn_fdf):
                raise RuntimeError('Could not find the fdf-file. It is required as '
                                   'part of the input for denchar.')

            fdf_mtime = getmtime(fn_fdf)
            for suf in ['.WFS', '.PLD', '.DM', '.DIM']:
                if not isfile(label+suf):
                    raise RuntimeError('Could not find file "%s%s" which is required '
                                       'when extracting wave functions '
                                       '(make sure the fdf options "WriteDenchar" is '
                                       'True, and WaveFuncKpoints is [0.0 0.0 0.0]")' %
                                       (label, suf))
                if not getmtime(label+suf) > fdf_mtime:
                    # This should be handled in a better way, e.g. by implementing
                    # a "calculation_required() and calculate()"
                    raise RuntimeError('The calculation is not up to date.')

            # Simply read the old fdf-file and pick some meta info from there.
            # However, strictly it's not always neccesary
            fdf = read_fdf(fn_fdf)
            if 'latticeconstant' in fdf:
                const = float(fdf['latticeconstant'][0])
                unit =  fdf['latticeconstant'][1]
            else:
                const = 1.0
                unit = 'Ang'

            if 'latticevectors' in fdf:
                cell = np.array(fdf['latticevectors'], dtype='d')
            else:
                raise RuntimeError('Failed to find the lattice vectors in the fdf-file.')

            if 'spinpolarized' in fdf and \
                    fdf['spinpolarized'][0].lower() in ['yes', 'true', '.true.', 'T', '']:
                if spin is None:
                    raise RuntimeError('The calculation was spin polarized, pick either '
                                       'spin=0 or 1.')
            else:
                if not spin is None:
                    raise RuntimeError('The calculation was not spin polarized, '
                                       'spin argument must be None.')

            denc_fdf = open(fn_fdf).readlines()
            denc_fdf.append('Denchar.TypeOfRun 3D\n')
            denc_fdf.append('Denchar.PlotWaveFunctions T\n')
            for dim, dir in zip(cell.transpose(), ['X', 'Y', 'Z']):
                # Naive square box limits to denchar
                denc_fdf.append('Denchar.Min%s %f %s\n' % (dir, const*dim.min(), unit))
                denc_fdf.append('Denchar.Max%s %f %s\n' % (dir, const*dim.max(), unit))

            # denchar rewinds stdin and fails if stdin is a pipe
            denc_fdf_file = open(label+'.denchar.fdf', 'w')
            denc_fdf_file.write(''.join(denc_fdf))
            denc_fdf_file.close()

            try:
                from subprocess import Popen, PIPE
                p = Popen('denchar', shell=True, stdin=open(label+'.denchar.fdf'),
                          stdout=PIPE, stderr=PIPE, close_fds=True)
                exitcode = p.wait()
            except ImportError:
                raise RuntimeError('get_pseudo_wave_function implemented only with subprocess.')

            if exitcode == 0:
                if not isfile(fn_wf):
                    raise RuntimeError('Could not find the requested file (%s)'%fn_wf)
                x, _ = read_cube_data(fn_wf)
                return x
            elif exitcode == 127:
                raise RuntimeError('No denchar executable found. Make sure it is in the path.')
            else:
                import sys
                print(''.join(p.stderr.readlines()), file=sys.stderr)
                raise RuntimeError('Execution of denchar failed!')

        def calculate(self, atoms):
            self.positions = atoms.get_positions().copy()
            self.cell = atoms.get_cell().copy()
            self.pbc = atoms.get_pbc().copy()

            if self.write_fdf_file:
                self.write_fdf(atoms)

            siesta = os.environ['SIESTA_SCRIPT']
            locals = {'label': self.label}
            exec(compile(open(siesta).read(), siesta, 'exec'), {}, locals)
            exitcode = locals['exitcode']
            if exitcode != 0:
                raise RuntimeError(('Siesta exited with exit code: %d.  ' +
                                    'Check %s.txt for more information.') %
                                   (exitcode, self.label))

            self.dipole = self.read_dipole()
            self.read()

            atoms_structout = read_struct('%s.STRUCT_OUT' % self.label)
            atoms.cell = atoms_structout.cell
            atoms.positions = atoms_structout.positions

            self.converged = True

        def set_fdf(self, key, value):
            """Set FDF parameter."""
            self.fdf[key] = value

        def write_fdf(self, atoms):
            """Write input parameters to fdf-file."""
            fh = open(self.label + '.fdf', 'w')

            fdf = {
                'SystemLabel': self.label,
                'AtomicCoordinatesFormat': 'Ang',
                'LatticeConstant': 1.0,
                'NumberOfAtoms': len(atoms),
                'MeshCutoff': self.meshcutoff,
                'NetCharge': self.charge,
                'ElectronicTemperature': self.width,
                'NumberOfEigenStates': self.nbands,
                'DM.UseSaveDM': self.converged,
                'PAO.BasisSize': self.basis,
                'SolutionMethod': 'diagon',
                'DM.NumberPulay': self.pulay,
                'DM.MixingWeight': self.mix,
                'MaxSCFIterations': self.maxiter
                }

            if self.xc != 'LDA':
                fdf['xc.functional'] = 'GGA'
                fdf['xc.authors'] = self.xc

            magmoms = atoms.get_initial_magnetic_moments()
            if magmoms.any():
                fdf['SpinPolarized'] = True
                fh.write('%block InitSpin\n')
                for n, M in enumerate(magmoms):
                    if M != 0:
                        fh.write('%d %.14f\n' % (n + 1, M))
                fh.write('%endblock InitSpin\n')

            fdf['Number_of_species'] = len(self.species)

            fdf.update(self.fdf)

            for key, value in fdf.items():
                if value is None:
                    continue

                if isinstance(value, list):
                    fh.write('%%block %s\n' % key)
                    for line in value:
                        fh.write(line + '\n')
                    fh.write('%%endblock %s\n' % key)
                else:
                    unit = keys_with_units.get(fdfify(key))
                    if unit is None:
                        fh.write('%s %s\n' % (key, value))
                    else:
                        if 'fs**2' in unit:
                            value /= fs**2
                        elif 'fs' in unit:
                            value /= fs
                        fh.write('%s %f %s\n' % (key, value, unit))

            fh.write('%block LatticeVectors\n')
            for v in self.cell:
                fh.write('%.14f %.14f %.14f\n' % tuple(v))
            fh.write('%endblock LatticeVectors\n')

            fh.write('%block Chemical_Species_label\n')
            for n, Z in enumerate(self.species):
                fh.write('%d %s %s\n' % (n + 1, Z, chemical_symbols[abs(Z)]))
            fh.write('%endblock Chemical_Species_label\n')

            fh.write('%block AtomicCoordinatesAndAtomicSpecies\n')
            a = 0
            for pos, Z in zip(self.positions, self.numbers):
                if a in self.ghosts:
                    Z = -Z
                a += 1
                fh.write('%.14f %.14f %.14f' %  tuple(pos))
                fh.write(' %d\n' % (self.species.index(Z) + 1))
            fh.write('%endblock AtomicCoordinatesAndAtomicSpecies\n')

            if self.kpts is not None:
                fh.write('%block kgrid_Monkhorst_Pack\n')
                for i in range(3):
                    for j in range(3):
                        if i == j:
                            fh.write('%d ' % self.kpts[i])
                        else:
                            fh.write('0 ')
                    fh.write('%.1f\n' % (((self.kpts[i] + 1) % 2) * 0.5))
                fh.write('%endblock kgrid_Monkhorst_Pack\n')

            fh.close()

        def read(self):
            """Read results from SIESTA's text-output file."""
            text = open(self.label + '.txt', 'r').read().lower()
            assert 'error' not in text
            lines = iter(text.split('\n'))

            # Get the number of grid points used:
            for line in lines:
                if line.startswith('initmesh: mesh ='):
                    self.grid = [int(word) for word in line.split()[3:8:2]]
                    break

            # Stress (fixed so it's compatible with a MD run from siesta):
            for line in lines:
                if line.startswith('siesta: stress tensor '):
                    stress = np.empty((3, 3))
                    for i in range(3):
                        tmp = lines.next().split()
                        if len(tmp) == 4:
                            stress[i] = [float(word) for word in tmp[1:]]
                        else:
                            stress[i] = [float(word) for word in tmp]
                    self.stress = np.array(
                        [stress[0, 0], stress[1, 1], stress[2, 2],
                         stress[1, 2], stress[0, 2], stress[0, 1]])
                    break
            else:
                raise RuntimeError

            text = open(self.label + '.txt', 'r').read().lower()
            lines = iter(text.split('\n'))
            # Energy (again a fix to make it compatible with a MD run from siesta):
            counter = 0
            for line in lines:
                if line.startswith('siesta: etot    =') and counter == 0:
                    counter += 1
                elif line.startswith('siesta: etot    ='):
                    self.etotal = float(line.split()[-1])
                    self.efree = float(lines.next().split()[-1])
                    break
            else:
                raise RuntimeError

            # Forces (changed so forces smaller than -999eV/A can be fetched):
            lines = open(self.label + '.FA', 'r').readlines()
            assert int(lines[0]) == len(self.numbers)
            assert len(lines) == len(self.numbers) + 1
            lines = lines[1:]
            self.forces = np.zeros((len(lines), 3))
            for i in range(len(lines)):
                self.forces[i, 0] = float(lines[i][6:18].strip())
                self.forces[i, 1] = float(lines[i][18:30].strip())
                self.forces[i, 2] = float(lines[i][30:42].strip())

        def read_eig(self):
            if self.e_fermi is not None:
                return

            assert os.access(self.label + '.EIG', os.F_OK)
            assert os.access(self.label + '.KP', os.F_OK)

            # Read k point weights
            text = open(self.label + '.KP', 'r').read()
            lines = text.split('\n')
            n_kpts = int(lines[0].strip())
            self.weights = np.zeros((n_kpts,))
            for i in range(n_kpts):
                l = lines[i + 1].split()
                self.weights[i] = float(l[4])

            # Read eigenvalues and fermi-level
            text = open(self.label+'.EIG','r').read()
            lines = text.split('\n')
            self.e_fermi = float(lines[0].split()[0])
            tmp = lines[1].split()
            self.n_bands = int(tmp[0])
            n_spin_bands = int(tmp[1])
            self.spin_pol = n_spin_bands == 2
            lines = lines[2:-1]
            lines_per_kpt = (self.n_bands * n_spin_bands / 10 +
                             int((self.n_bands * n_spin_bands) % 10 != 0))
            self.eig = dict()
            for i in range(len(self.weights)):
                tmp = lines[i * lines_per_kpt:(i + 1) * lines_per_kpt]
                v = [float(v) for v in tmp[0].split()[1:]]
                for l in tmp[1:]:
                    v.extend([float(t) for t in l.split()])
                if self.spin_pol:
                    self.eig[(i, 0)] = np.array(v[0:self.n_bands])
                    self.eig[(i, 1)] = np.array(v[self.n_bands:])
                else:
                    self.eig[(i, 0)] = np.array(v)

        def get_k_point_weights(self):
            self.read_eig()
            return self.weights

        def get_fermi_level(self):
            self.read_eig()
            return self.e_fermi

        def get_eigenvalues(self, kpt=0, spin=0):
            self.read_eig()
            return self.eig[(kpt, spin)]

        def get_number_of_spins(self):
            self.read_eig()
            if self.spin_pol:
                return 2
            else:
                return 1

        def read_hs(self, filename, is_gamma_only=False, magnus=False):
            """Read the Hamiltonian and overlap matrix from a Siesta
               calculation in sparse format.

            Parameters
            ==========
            filename: str
                The filename should be on the form jobname.HS
            is_gamma_only: {False, True), optional
                Is it a gamma point calculation?
            magnus: bool
                The fileformat was changed by Magnus in Siesta at some
                point around version 2.xxx.
                Use mangus=False, to use the old file format.

            Note
            ====
            Data read in is put in self._dat.

            Examples
            ========
                >>> calc = Siesta()
                >>> calc.read_hs('jobname.HS')
                >>> print calc._dat.fermi_level
                >>> print 'Number of orbitals: %i' % calc._dat.nuotot
            """
            assert not magnus, 'Not implemented; changes by Magnus to file io'
            assert not is_gamma_only, 'Not implemented. Only works for k-points.'
            class Dummy:
                pass
            self._dat = dat = Dummy()
            # Try to read supercell and atom data from a jobname.XV file
            filename_xv = filename[:-2] + 'XV'
            #assert isfile(filename_xv), 'Missing jobname.XV file'
            if isfile(filename_xv):
                print('Reading supercell and atom data from ' + filename_xv)
                fd = open(filename_xv, 'r')
                dat.cell = np.zeros((3, 3)) # Supercell
                for a_vec in dat.cell:
                    a_vec[:] = np.array(fd.readline().split()[:3], float)
                dat.rcell = 2 * np.pi * np.linalg.inv(dat.cell.T)
                dat.natoms = int(fd.readline().split()[0])
                dat.symbols = []
                dat.pos_ac = np.zeros((dat.natoms, 3))
                for a in range(dat.natoms):
                    line = fd.readline().split()
                    dat.symbols.append(chemical_symbols[int(line[1])])
                    dat.pos_ac[a, :] = [float(line[i]) for i in range(2, 2 + 3)]
            # Read in the jobname.HS file
            fileobj = file(filename, 'rb')
            fileobj.seek(0)
            dat.fermi_level = float(open(filename[:-3] + '.EIG', 'r').readline())
            dat.is_gammay_only = is_gamma_only
            dat.nuotot, dat.ns, dat.mnh = getrecord(fileobj, 'l')
            nuotot, ns, mnh = dat.nuotot, dat.ns, dat.mnh
            print('Number of orbitals found: %i' % nuotot)
            dat.numh = numh = np.array([getrecord(fileobj, 'l')
                                        for i in range(nuotot)], 'l')
            dat.maxval = max(numh)
            dat.listhptr = listhptr = np.zeros(nuotot, 'l')
            listhptr[0] = 0
            for oi in range(1, nuotot):
                listhptr[oi] = listhptr[oi - 1] + numh[oi - 1]
            dat.listh = listh = np.zeros(mnh, 'l')

            print('Reading sparse info')
            for oi in range(nuotot):
                for mi in range(numh[oi]):
                    listh[listhptr[oi] + mi] = getrecord(fileobj, 'l')

            dat.nuotot_sc = max(listh)
            dat.h_sparse = h_sparse = np.zeros((mnh, ns), float)
            dat.s_sparse = s_sparse = np.zeros(mnh, float)
            print('Reading H')
            for si in range(ns):
                for oi in range(nuotot):
                    for mi in range(numh[oi]):
                        h_sparse[listhptr[oi] + mi, si] = getrecord(fileobj, 'd')
            print('Reading S')
            for oi in range(nuotot):
                for mi in range(numh[oi]):
                    s_sparse[listhptr[oi] + mi] = getrecord(fileobj, 'd')

            dat.qtot, dat.temperature = getrecord(fileobj, 'd')
            if not is_gamma_only:
                print('Reading X')
                dat.xij_sparse = xij_sparse = np.zeros([3, mnh], float)
                for oi in range(nuotot):
                    for mi in range(numh[oi]):
                        xij_sparse[:, listhptr[oi] + mi] = getrecord(fileobj, 'd')
            fileobj.close()

        def get_hs(self, kpt=(0, 0, 0), spin=0, remove_pbc=None, kpt_scaled=True):
            """Hamiltonian and overlap matrices for an arbitrary k-point.

            The default values corresponds to the Gamma point for
            spin 0 and periodic boundary conditions.

            Parameters
            ==========
            kpt : {(0, 0, 0), (3,) array_like}, optional
                k-point in scaled or absolute coordinates.
                For the latter the units should be Bohr^-1.
            spin : {0, 1}, optional
                Spin index
            remove_pbc : {None, ({'x', 'y', 'z'}, basis)}, optional
                Use remove_pbc to truncate h and s along a cartesian
                axis.
            basis: {str, dict}
                The basis specification as either a string or a dictionary.
            kpt_scaled : {True, bool}, optional
                Use kpt_scaled=False if `kpt` is in absolute units (Bohr^-1).

            Note
            ====
            read_hs should be called before get_hs gets called.

            Examples
            ========
            >>> calc = Siesta()
            >>> calc.read_hs('jobname.HS')
            >>> h, s = calc.get_hs((0.0, 0.375, 0.375))
            >>> h -= s * calc._dat.fermi_level # fermi level is now at 0.0
            >>> basis = 'szp'
            >>> h, s = calc.get_hs((0.0, 0.375, 0.375), remove_pbc=('x', basis))
            >>> basis = {'Au:'sz}', 'C':'dzp', None:'szp'}
            >>> h, s = calc.get_hs((0.0, 0.375, 0.375), remove_pbc=('x', basis))

            """
            if not hasattr(self, '_dat'):# XXX Crude check if data is avail.
                print('Please read in data first by calling the method read_hs.')
                return None, None
            dot = np.dot
            dat = self._dat
            kpt_c = np.array(kpt, float)
            if kpt_scaled:
                kpt_c = dot(kpt_c, dat.rcell)

            h_MM = np.zeros((dat.nuotot, dat.nuotot), complex)
            s_MM = np.zeros((dat.nuotot, dat.nuotot), complex)
            h_sparse, s_sparse = dat.h_sparse, dat.s_sparse
            x_sparse = dat.xij_sparse
            numh, listhptr, listh = dat.numh, dat.listhptr, dat.listh
            indxuo = np.mod(np.arange(dat.nuotot_sc), dat.nuotot)

            for iuo in range(dat.nuotot):
                for j in range(numh[iuo]):
                    ind =  listhptr[iuo] + j
                    jo = listh[ind] - 1
                    juo = indxuo[jo]
                    kx = dot(kpt_c, x_sparse[:, ind])
                    phasef = exp(1.0j * kx)
                    h_MM[iuo, juo] += phasef * h_sparse[ind, spin]
                    s_MM[iuo, juo] += phasef * s_sparse[ind]

            if remove_pbc is not None:
                direction, basis = remove_pbc
                centers_ic = get_bf_centers(dat.symbols, dat.pos_ac, basis)
                d = 'xyz'.index(direction)
                cutoff = dat.cell[d, d] * 0.5
                truncate_along_axis(h_MM, s_MM, direction, centers_ic, cutoff)

            h_MM *= complex(Rydberg)
            return h_MM, s_MM

    def getrecord(fileobj, dtype):
        """Used to read in binary files.
        """
        typetosize = {'l':4, 'f':4, 'd':8}# XXX np.int, np.float32, np.float64
        assert dtype in typetosize # XXX
        size = typetosize[dtype]
        record = array.array('l')
        trunk = array.array(dtype)
        record.fromfile(fileobj, 1)
        nofelements = int(record[-1]) / size
        trunk.fromfile(fileobj, nofelements)
        record.fromfile(fileobj, 1)
        data = np.array(trunk, dtype=dtype)
        if len(data)==1:
            data = data[0]
        return data

    def truncate_along_axis(h, s, direction, centers_ic, cutoff):
        """Truncate h and s such along a cartesian axis.

        Parameters:

        h: (N, N) ndarray
            Hamiltonian matrix.
        s: (N, N) ndarray
            Overlap matrix.
        direction: {'x', 'y', 'z'}
            Truncate allong a cartesian axis.
        centers_ic: (N, 3) ndarray
            Centers of the basis functions.
        cutoff: float
            The (direction-axis projected) cutoff distance.
        """
        dtype = h.dtype
        ni = len(centers_ic)
        d = 'xyz'.index(direction)
        pos_i = centers_ic[:, d]
        for i in range(ni):
            dpos_i = abs(pos_i - pos_i[i])
            mask_i = (dpos_i < cutoff).astype(dtype)
            h[i, :] *= mask_i
            h[:, i] *= mask_i
            s[i, :] *= mask_i
            s[:, i] *= mask_i

    def get_nao(symbol, basis):
        """Number of basis functions.

        Parameters
        ==========
        symbol: str
            The chemical symbol.
        basis: str
            Basis function type.
        """
        ls = valence_config[symbol]
        nao = 0
        zeta = {'s':1, 'd':2, 't':3, 'q':4}
        nzeta = zeta[basis[0]]
        is_pol = 'p' in basis
        for l in ls:
            nao += (2 * l + 1) * nzeta
        if is_pol:
            l_pol = None
            l = -1
            while l_pol is None:
                l += 1
                if not l in ls:
                    l_pol = l
            nao += 2 * l_pol + 1
        return nao

    def get_bf_centers(symbols, positions, basis):
        """Centers of basis functions.

        Parameters
        ==========
        symbols: str, (N, ) array_like
            chemical symbol for each atom.
        positions: float, (N, 3) array_like
            Positions of the atoms.
        basis: {str,  dict}
            Basis set specification as either a string or a dictionary

        Examples
        ========
        >>> symbols = ['O', 'H']
        >>> positions = [(0, 0, 0), (0, 0, 1)]
        >>> basis = 'sz'
        >>> print get_bf_centers(symbols, positions, basis)
        [[0 0 0]
         [0 0 0]
         [0 0 0]
         [0 0 0]
         [0 0 1]]
        >>> basis = {'H':'dz', None:'sz'}
        >>> print get_bf_centers(symbols, positions, basis)
        [[0 0 0]
         [0 0 0]
         [0 0 0]
         [0 0 0]
         [0 0 1]
         [0 0 1]]

        """
        centers_ic = []
        dict_basis = False
        if isinstance(basis, dict):
            dict_basis = True
        for symbol, pos in zip(symbols, positions):
            if dict_basis:
                if symbol not in basis:
                    bas = basis[None]
                else:
                    bas = basis[symbol]
            else:
                bas = basis
            for i in range(get_nao(symbol, bas)):
                centers_ic.append(pos)
        return np.asarray(centers_ic)

    def fdfify(key):
        return key.lower().replace('_', '').replace('.', '').replace('-', '')

    valence_config = {
        'H': (0,),
        'C': (0, 1),
        'N': (0, 1),
        'O': (0, 1),
        'S': (0, 1),
        'Li': (0,),
        'Na': (0,),
        'Ni': (0, 2),
        'Cu': (0, 2),
        'Pd': (0, 2),
        'Ag': (0, 2),
        'Pt': (0, 2),
        'Au': (0, 2)}

    keys_with_units = {
        'paoenergyshift': 'eV',
        'zmunitslength': 'Bohr',
        'zmunitsangle': 'rad',
        'zmforcetollength': 'eV/Ang',
        'zmforcetolangle': 'eV/rad',
        'zmmaxdispllength': 'Ang',
        'zmmaxdisplangle': 'rad',
        'meshcutoff': 'eV',
        'dmenergytolerance': 'eV',
        'electronictemperature': 'eV',
        'oneta': 'eV',
        'onetaalpha': 'eV',
        'onetabeta': 'eV',
        'onrclwf': 'Ang',
        'onchemicalpotentialrc': 'Ang',
        'onchemicalpotentialtemperature': 'eV',
        'mdmaxcgdispl': 'Ang',
        'mdmaxforcetol': 'eV/Ang',
        'mdmaxstresstol': 'eV/Ang**3',
        'mdlengthtimestep': 'fs',
        'mdinitialtemperature': 'eV',
        'mdtargettemperature': 'eV',
        'mdtargetpressure': 'eV/Ang**3',
        'mdnosemass': 'eV*fs**2',
        'mdparrinellorahmanmass': 'eV*fs**2',
        'mdtaurelax': 'fs',
        'mdbulkmodulus': 'eV/Ang**3',
        'mdfcdispl': 'Ang',
        'warningminimumatomicdistance': 'Ang',
        'rcspatial': 'Ang',
        'kgridcutoff': 'Ang',
        'latticeconstant': 'Ang'}

### Navigation

- index
- modules |
- Module code »
- ase.calculators »