Exemple #1
0
def build_operators(spos, trans, check_mapping=False, eps=1e-6):
    '''Given a list of fractional positions, and a list of
    fractional translations, produce a set of matrices, 
    describing how fractional translations permute atomic
    positions within the unit cell. Two fractional positions
    si and sj are considered to be identical when |si-sj|<eps.
    '''
    ntrans = len(trans)
    natoms = len(spos)

    ops = np.zeros((ntrans, natoms, natoms), int)

    for i, ti in enumerate(trans):
        for j, sj in enumerate(spos):
            for k, sk in enumerate(spos):
                # If displacement between two atomic positions
                # differs from the fractional translations by
                # by a lattice translation+-eps we consider the
                # that two atomic positions to be map onto each
                # other by the fractional translation
                disp = sk - sj - ti
                ops[i, j, k] = np.linalg.norm(disp - np.rint(disp)) < eps

    if check_mapping:
        # Every row and every column of every operator must
        # contain exactly one unity, otherwise translations
        # are not maping atoms one-to-one.
        if np.any(np.sum(ops, axis=1) != 1) or np.any(
                np.sum(ops, axis=2) != 1):
            post_error('Translations are not one-to-one. '
                       'Try changing the matching tolerance, or try using '
                       'the POSCAR file with more regular positions.')

    return ops
Exemple #2
0
def build_operators(spos, trans, check_mapping=False, eps=1e-6):
    """Given a list of fractional positions, and a list of
    fractional translations, produce a set of matrices, 
    describing how fractional translations permute atomic
    positions within the unit cell. Two fractional positions
    si and sj are considered to be identical when |si-sj|<eps.
    """
    ntrans = len(trans)
    natoms = len(spos)

    ops = np.zeros((ntrans, natoms, natoms), int)

    for i, ti in enumerate(trans):
        for j, sj in enumerate(spos):
            for k, sk in enumerate(spos):
                # If displacement between two atomic positions
                # differs from the fractional translations by
                # by a lattice translation+-eps we consider the
                # that two atomic positions to be map onto each
                # other by the fractional translation
                disp = sk - sj - ti
                ops[i, j, k] = np.linalg.norm(disp - np.rint(disp)) < eps

    if check_mapping:
        # Every row and every column of every operator must
        # contain exactly one unity, otherwise translations
        # are not maping atoms one-to-one.
        if np.any(np.sum(ops, axis=1) != 1) or np.any(np.sum(ops, axis=2) != 1):
            post_error(
                "Translations are not one-to-one. "
                "Try changing the matching tolerance, or try using "
                "the POSCAR file with more regular positions."
            )

    return ops
Exemple #3
0
def parse_poscar(filename):
    '''Parses POSCAR file. Returns 3x3 float array
    containing unit cell vectors as rows, Nx3
    array containing fractional positions of atoms,
    N being number of atoms and a list of chemical
    symbols.
    '''
    try:
        gl = Getlines(filename)
    except:
        post_error('Unable to open "{0}" for reading.'.format(filename))

    # Skip the comment line
    gl.get_next()

    # Read the scaling factor
    f = float(gl.get_next())

    # Read the unit cell vectors
    cell = np.zeros((3, 3), float)

    cell[0] = f * np.array(gl.get_next().split(), float)
    cell[1] = f * np.array(gl.get_next().split(), float)
    cell[2] = f * np.array(gl.get_next().split(), float)

    # Read the chemical symbols
    syms = gl.get_next().split()

    # Read the atom counts
    counts = np.array(gl.get_next().split(), int)

    # Get the number of atoms
    natoms = np.sum(counts)

    # Rearrange into the long list of chemical symbols
    symbols = []

    for s, c in zip(syms, counts):
        symbols += [s] * c

    # Cartesian or fractional coordinates?
    ctype = gl.get_next()[0].lower()

    if ctype == 'c':
        mult = np.linalg.inv(cell)
    elif ctype == 'd':
        mult = np.eye(3)
    else:
        post_error('"{0}" is unknown POSCAR option'.format(plines[7].strip()))

    # Allocate storage for positions
    spos = np.zeros((len(symbols), 3))

    # Read the positions
    for i in xrange(len(symbols)):
        spos[i] = np.array(gl.get_next().split()[:3], float)

    return cell, spos, symbols
def main():
    desc_str = 'Unfold bands calculated by VASP. For this, phase '\
               'information needs to be present in the PROCAR file '\
               'which means that bands need to be calculated with '\
               'the option LORBIT=12 specified in the INCAR file. '\
               'The required input consists of POSCAR file, PROCAR '\
               'file and a set of up to three fractional translations. '\
               'There is no need to specify all translations, just the '\
               'generators. The programs will generate all distinct '\
               'translations from the generators and generate all irreps. '\
               'NOTE: In cas of non-collinear spin-polarized calculations ' \
               'mx, my and mz components of orbital weights are not ' \
               'unfolded, only spin up and spin down totals.'
               
               
    parser = argparse.ArgumentParser(prog='vasp_unfold', description = desc_str)

    parser.add_argument('poscar', type=str, help='POSCAR file')     
    parser.add_argument('procar', type=str, help='PROCAR file')

    parser.add_argument('--tgen', type=translation, action='append',
                        metavar='SX,SY,SZ', help='Fractional translation '
                        'generator. No whitespaces are allowed between the '
                        'components! SX, SY and SZ can be either 0 or 1/n, '
                        'where n is an integer. Up to three linearly '
                        'independant generators can be specified.')

    parser.add_argument('--out', type=str, help='Output filename. If left '
                        'unspecified  output is writen to PROCAR.irrep.n '
                        'where PROCAR is location of the input PROCAR file. '
                        'If specified, output is written to OUT.irrep.n.')
    
    parser.add_argument('--eps', type=float, default=1e-6, help='Numerical '
                        'precision. When building permutation representation '
                        'of the fractional translations this parameter is used '
                        'to determine whether two fractional positions are '
                        'identical. For irregular structures, it may need to '
                        'be increased. If this does not help, try to tweak '
                        'the atomic positions in POSCAR to make the structure '
                        'more regular. Default is 1e-6.')
    
    parser.add_argument('--all-irreps', default=False, action='store_true',
                        help='Flag specifying that all irreps from the unfolding '
                        'will be written to the output. By default, only irrep 0 '
                        '(unit irrep) is written out.')
                        
    parser.add_argument('--check-mapping', action='store_true', default=False,
                        help='Specifies to check if supplied translation '
                        'generators generate fractional translations which are '
                        'one-to-one, ie. map every atom on exactly one other atom '
                        'in the unit cell. This MUST not be enabled for the cases '
                        'where vacancies or excess atoms are present.')
                                                                     
    args = parser.parse_args()
    
    tgens = args.tgen
    
    trans, irreps = build_translations(tgens)
    
    try:
        cell, spos, symbols = parse_poscar(args.poscar)
    except:
        post_error('Unable to parse the input POSCAR file. Please '
            'check if the file exists and is formatted properly')
    
    ops = build_operators(spos, trans, args.check_mapping, args.eps)
    
    try:
        data = parse_procar(args.procar)
    except:
        post_error(errors.poscar_parse_error)
    
    if data[-1] is None:
        post_error('Phase information has to be present in the PROCAR '
            'file. Please repeat the calculation with LORBIT=12.')

    phases = np.copy(data[-1])
    
    norbs = phases.shape[1]/len(spos)
    
    projs = build_projectors(irreps, ops, norbs)
    
    if args.out is None:
        output = args.procar
    else:
        output = args.out
    
    if args.all_irreps:
        nirrep = len(projs)
    else:
        nirrep = 1
        
    for i, p in enumerate(projs[:nirrep]):
        for s in xrange(data[-1].shape[-1]):
            try:
                data[-1][:,:,:,s] = np.dot(p, phases[:,:,:,s]).swapaxes(0, 1)
            except:
                post_error('Unable to apply projectors. Are you sure '
                    'that specified POSCAR and PROCAR file belong to '
                    'the same crystal structure?')
        
        # We update total absolute weights to correspond to
        # unfolded phases (by multiplying them by the magnitude
        # ratio of unfolded and folded phases 
        phase_ratio = np.abs(data[-1])/(np.abs(phases)+1e-4)
        
        for idim in xrange(data[-2].shape[3]):
            data[-2][:,:,:,idim,:] *= phase_ratio

        write_procar('{0}.irrep.{1}'.format(output, i), *data)
Exemple #5
0
def write_procar(fname, orbitals, kpoints, kweights, bands, 
                 occupations, weights, phases):
    '''Write PROCAR file based on supplied data
    '''
    # Labels for orbitals
    orblabels = ['s', 'py', 'pz', 'px', 'dxy', 'dyz', 'dz2', 'dxz', 'dx2',
                 'f-3', 'f-2', 'f-1', 'f0', 'f1', 'f2', 'f3']
    
    norb = len(orbitals)
    npoints = len(kpoints)
    nbands = bands.shape[1]
    nions = weights.shape[1]/norb
    nspin = weights.shape[-1]
    ndim = weights.shape[-2]
    
    try:
        out = open(fname, 'w')
    except:
        post_error('Unable to open "{0}" for writing'.format(fname))
    
    # Write the first line of the PROCAR file
    if phases is not None:
        out.write('PROCAR lm decomposed + phase\n')
    else:
        out.write('PROCAR lm decomposed\n')

    # Format out the column title line for orbital weights
    orb_ttl_1 = 'ion '
    orb_ttl_2 = 'ion '
    
    for i in xrange(norb):
        orb_ttl_1 += '{0: >6} '.format(orblabels[i])
        orb_ttl_2 += '{0: >6} '.format(orblabels[i])
    
    orb_ttl_1 += '{0: >6}\n'.format('tot')
    orb_ttl_2 += '\n'
    
    for s in xrange(nspin):
        # Write the second line containing the sizes
        out.write('# of k-points:  {0}         # of bands:  {1}'
              '         # of ions:   {2}\n\n'.format(npoints, nbands, nions))
              
        for i, k in enumerate(kpoints):
            # Write the k-point info
            out.write(' k-point {0: >4} :    '.format(i+1))
            out.write('{0:.8f} {1:.8f} {2:.8f}     '.format(*k))
            out.write('weight = {0:.8f}\n\n'.format(kweights[i]))
            
            for j, b in enumerate(bands[i,:,s]):
                # Write the band info
                out.write('band {0: >4} # '.format(j+1))
                out.write('energy {0: >13.8f} # '.format(b))
                out.write('occ. {0: >11.8f}\n\n'.format(occupations[i, j, s]))
                
                # Write absolute weight blocks. In case of 
                # non-collinear calculation, there is four
                # such blocks
                for d in xrange(ndim):
                    if d == 0:
                        # Write names of orbitals (s, px, py etc...)
                        out.write(orb_ttl_1)
                    
                    # Allocate storage for accumulation of orbital totals
                    tot_orb = np.zeros(norb, float)
                    
                    # Loop over individual atoms
                    for k in xrange(nions):
                        # Write atom's index
                        out.write('{0: >3} '.format(k+1))
                        
                        # Extract corresponding weights
                        w = weights[i, k*norb:(k+1)*norb, j,d,s]
                        
                        # Add to totals
                        tot_orb += w
                        
                        # Write out row of weights
                        for l in xrange(norb):
                            out.write('{0: >6.3f} '.format(w[l]))
                        
                        # Write atom total
                        out.write('{0: >6.3f}\n'.format(np.sum(w)))
                    
                    # We will now write line with orbital totals
                    out.write('tot ')
                    
                    for l in xrange(norb):
                        out.write('{0: >6.3f} '.format(tot_orb[l]))
                    
                    # Finally, atom+orbital total
                    out.write('{0: >6.3f}\n'.format(np.sum(tot_orb)))
                
                # If we have phases we write them now
                if phases is not None:
                    # Write again names of orbitals
                    out.write(orb_ttl_2)
                    
                    # Loop over atoms
                    for k in xrange(nions):
                        # Write atom index
                        out.write('{0: >3} '.format(k+1))
                        
                        # Extract corresponding phase
                        phs = phases[i, k*norb:(k+1)*norb, j, s]
                        
                        # Write real part
                        for l in xrange(norb):
                            out.write('{0: >6.3f} '.format(phs[l].real))
                        
                        # Write atom index
                        out.write('\n{0: >3} '.format(k+1))
                        
                        # Write imaginary part
                        for l in xrange(norb):
                            out.write('{0: >6.3f} '.format(phs[l].imag))
                        
                        out.write('\n')
                    
                    out.write('\n')
                
            out.write('\n')
                
    out.close()
Exemple #6
0
def parse_procar(filename):
    '''This function parses a PROCAR file. It returns a tuple
    consisting of following elements:
    
    orbitals    - (norbs) string array of orbital labels (s, px, py etc...)
    kpoints     - (npoints,3) float array of k-point coordinates
    kweights    - (npoints) float array of k-point weights
    bands       - (npoints,nbands,nspin) float array of band energies
    occupancies - (npoints,nbands,nspin) float array of band occupancies
    weights     - (npoints,nions*norbs,nbands,ndim,nspin) float array
                  of orbital weights
    phases      - (npoints,nions*norbs,nbands,nspin) complex array of
                  phases of orbital weights if LORBIT=12, otherwise None
                  
    Where:
    
    norbs   - number of orbitals (It can be 9 or 16 with f orbitals)
    npoints - number of k-points
    nbands  - number of bands
    nspin   - number of spins (1 for non spin-polarized, 2 otherwise)
    nions   - number of atoms
    ndim    - orbital weight dimensionality (1 for collinear, 4 otherwise)
    '''

    try:
        gl = Getlines(filename)
    except:
        post_error('Unable to open "{0}" for reading.'.format(filename))

    header_1 = gl.get_next()
    header_2 = gl.get_next().split()

    npoints = int(header_2[3])
    nbands = int(header_2[7])
    nions = int(header_2[-1])

    # Remember the position in file
    start = gl.tell()

    # Skip two lines containing first k-point and band
    gl.get_next()
    gl.get_next()

    # Determine the number of orbitals
    orbitals = gl.get_next().split()[1:-1]

    norbs = len(orbitals)

    # Allocate maximal storage. In case calculation was
    # non spin-polarized or collinear we can just trim
    # the excess components at the end
    kpoints = np.zeros((npoints, 3), float)
    kweights = np.zeros(npoints, float)
    bands = np.zeros((npoints, nbands, 2), float)
    occupancies = np.zeros((npoints, nbands, 2), float)
    weights = np.zeros((npoints, nions * norbs, nbands, 4, 2), float)

    dim = 0

    # Determine if the calculation was non-collinear
    # by counting how many lines in the first band
    # block begin with tot. That number will be equal
    # to the number of sub-blocks for orbital weights
    # (1 in case of collinear and 4 otherwise)
    while True:
        line = gl.get_next()

        if line.startswith('tot'):
            dim += 1
        elif line.startswith('band'):
            break

    # This function will read block of absolute weights
    # for i-th k-point, j-th band and s-th spin component
    def get_absweights(i, j, s):
        # Skip line with orbital names
        gl.get_next()

        # k loops over total, mx, my and mz
        for k in xrange(dim):
            # l goes over ions
            for l in xrange(nions):
                weights[i,l*norbs:(l+1)*norbs,j,k,s] = \
                    np.array(gl.get_next().split()[1:-1], float)

            # Skip line with totals
            gl.get_next()

    # Check whether phase information is included
    if '+ phase' in header_1:
        # Allocate storage for phases
        phases = np.zeros((npoints, nions * norbs, nbands, 2), complex)

        # Declare nested function that handles
        # parsing of complex weights
        def get_weights(i, j, s):
            # Read abs values of weights
            get_absweights(i, j, s)

            # Skip line with orbital names
            gl.get_next()

            for k in xrange(nions):
                # Get real part
                phases[i,k*norbs:(k+1)*norbs,j,s] = \
                    np.array(gl.get_next().split()[1:], float)
                # Get imaginary part
                phases[i,k*norbs:(k+1)*norbs,j,s] += \
                    1j*np.array(gl.get_next().split()[1:], float)
    else:
        # Phases are None in this case
        phases = None

        # In this case we just have absolutes of weights
        # No need for a new function
        get_weights = get_absweights

    # Go back to the beginning of the first k-point
    gl.seek(start)

    for i in xrange(npoints):
        # Parse k-point coordinates
        temp = gl.get_next()
        problem_index = []
        for ic in range(len(temp) - 1):
            if temp[ic] <= '9' and temp[ic] >= '0' and temp[ic + 1] == '-':
                problem_index.append(ic)
        if len(problem_index):
            addition = 0
            for ic in problem_index:
                temp = temp[:ic + 1 + addition] + ' ' + temp[ic + 1 +
                                                             addition:]
                addition += 1
        print temp
        k_line = temp.split()
        kpoints[i] = np.array(k_line[3:6], float)
        kweights[i] = float(k_line[-1])

        for j in xrange(nbands):
            # Parse band energy
            band_line = gl.get_next().split()

            bands[i, j, 0] = float(band_line[4])
            occupancies[i, j, 0] = float(band_line[-1])

            # Parse orbital weights
            get_weights(i, j, 0)

    # Seek now for the second spin component
    res = gl.get_next(False)

    # If there is no second spin component, finish by
    # returning just the first component
    if res is None:
        # Trim the second spin component
        bands = bands[:, :, :1]
        occupancies = occupancies[:, :, :1]
        weights = weights[:, :, :, :dim, :1]

        if phases is not None:
            phases = phases[:, :, :, :1]

        return orbitals, kpoints, kweights, bands, occupancies, \
            weights, phases

    # Otherwise, read bands and weights for the second component
    for i in xrange(npoints):
        # Skip k-point coordinates
        gl.get_next()

        for j in xrange(nbands):
            # Parse band energy
            band_line = gl.get_next().split()

            bands[i, j, 1] = float(band_line[4])
            occupancies[i, j, 1] = float(band_line[-1])

            # Parse orbital weights
            get_weights(i, j, 1)

    return orbitals, kpoints, kweights, bands, occupancies, \
        weights[:,:,:,:dim,:], phases
Exemple #7
0
def write_procar(fname, orbitals, kpoints, kweights, bands, occupations,
                 weights, phases):
    '''Write PROCAR file based on supplied data
    '''
    # Labels for orbitals
    orblabels = [
        's', 'py', 'pz', 'px', 'dxy', 'dyz', 'dz2', 'dxz', 'dx2', 'f-3', 'f-2',
        'f-1', 'f0', 'f1', 'f2', 'f3'
    ]

    norb = len(orbitals)
    npoints = len(kpoints)
    nbands = bands.shape[1]
    nions = weights.shape[1] / norb
    nspin = weights.shape[-1]
    ndim = weights.shape[-2]

    try:
        out = open(fname, 'w')
    except:
        post_error('Unable to open "{0}" for writing'.format(fname))

    # Write the first line of the PROCAR file
    if phases is not None:
        out.write('PROCAR lm decomposed + phase\n')
    else:
        out.write('PROCAR lm decomposed\n')

    # Format out the column title line for orbital weights
    orb_ttl_1 = 'ion '
    orb_ttl_2 = 'ion '

    for i in xrange(norb):
        orb_ttl_1 += '{0: >6} '.format(orblabels[i])
        orb_ttl_2 += '{0: >6} '.format(orblabels[i])

    orb_ttl_1 += '{0: >6}\n'.format('tot')
    orb_ttl_2 += '\n'

    for s in xrange(nspin):
        # Write the second line containing the sizes
        out.write('# of k-points:  {0}         # of bands:  {1}'
                  '         # of ions:   {2}\n\n'.format(
                      npoints, nbands, nions))

        for i, k in enumerate(kpoints):
            # Write the k-point info
            out.write(' k-point {0: >4} :    '.format(i + 1))
            out.write('{0:.8f} {1:.8f} {2:.8f}     '.format(*k))
            out.write('weight = {0:.8f}\n\n'.format(kweights[i]))

            for j, b in enumerate(bands[i, :, s]):
                # Write the band info
                out.write('band {0: >4} # '.format(j + 1))
                out.write('energy {0: >13.8f} # '.format(b))
                out.write('occ. {0: >11.8f}\n\n'.format(occupations[i, j, s]))

                # Write absolute weight blocks. In case of
                # non-collinear calculation, there is four
                # such blocks
                for d in xrange(ndim):
                    # Write names of orbitals (s, px, py etc...)
                    out.write(orb_ttl_1)

                    # Allocate storage for accumulation of orbital totals
                    tot_orb = np.zeros(norb, float)

                    # Loop over individual atoms
                    for k in xrange(nions):
                        # Write atom's index
                        out.write('{0: >3} '.format(k + 1))

                        # Extract corresponding weights
                        w = weights[i, k * norb:(k + 1) * norb, j, d, s]

                        # Add to totals
                        tot_orb += w

                        # Write out row of weights
                        for l in xrange(norb):
                            out.write('{0: >6.3f} '.format(w[l]))

                        # Write atom total
                        out.write('{0: >6.3f}\n'.format(np.sum(w)))

                    # We will now write line with orbital totals
                    out.write('tot ')

                    for l in xrange(norb):
                        out.write('{0: >6.3f} '.format(tot_orb[l]))

                    # Finally, atom+orbital total
                    out.write('{0: >6.3f}\n'.format(np.sum(tot_orb)))

                # If we have phases we write them now
                if phases is not None:
                    # Write again names of orbitals
                    out.write(orb_ttl_2)

                    # Loop over atoms
                    for k in xrange(nions):
                        # Write atom index
                        out.write('{0: >3} '.format(k + 1))

                        # Extract corresponding phase
                        phs = phases[i, k * norb:(k + 1) * norb, j, s]

                        # Write real part
                        for l in xrange(norb):
                            out.write('{0: >6.3f} '.format(phs[l].real))

                        # Write atom index
                        out.write('\n{0: >3} '.format(k + 1))

                        # Write imaginary part
                        for l in xrange(norb):
                            out.write('{0: >6.3f} '.format(phs[l].imag))

                        out.write('\n')

                    out.write('\n')

            out.write('\n')

    out.close()
Exemple #8
0
def build_translations(tgens):
    '''Build a list of translations and irreps from at most 
    three linearly independent generators specified as lists
    of three fractions.
    '''
    if len(tgens) > 3:
        post_error('There can be at most three generators '
                   'of fractional translations.')

    # Use folded generators as floats to perform linear independece tests
    tgensf = np.array(tgens, dtype=float) % 1

    # Tolerance for linear independenec per dimension
    eps = 1e-2

    # Check if generators are linearly independent
    if len(tgens) == 2 and np.all(np.cross(tgensf[0], tgensf[1]) < eps**2):
        post_error('Generators are not linearly independant.')
    elif len(tgens) == 3 and np.linalg.det(np.array(tgensf)) < eps**3:
        post_error('Generators are not linearly independant.')

    # Expand the generator list to be a 3x3 matrix
    tgens = np.append(tgens, np.ones((3 - len(tgens), 3)), axis=0)

    # Get the order of every generator
    order = np.array([frac_translation_order(g) for g in tgens], int)

    # Get the corresponding roots of unity which will be
    # used to construct irreps
    deltag = np.exp(-2 * np.pi * 1j / order)

    # Fold the translation vectors into the unit cell
    tgens = tgens % 1

    # Total number of translations
    ntrans = np.prod(order)

    # Storage for translations
    trans = np.zeros((ntrans, 3), float)

    # Calculate irreps of every generator
    irrepg = np.zeros((ntrans, 3), complex)

    # Loop over all possible products of powers of generators
    # and store the irreps
    for i in xrange(order[0]):
        for j in xrange(order[1]):
            for k in xrange(order[2]):
                ii = i * order[1] * order[2] + j * order[2] + k

                irrepg[ii] = deltag**[i, j, k]

    # Irrep table for all translations
    irreps = np.zeros((ntrans, ntrans), complex)

    # Loop over all posible products of powers of generators
    for i in xrange(order[0]):
        for j in xrange(order[1]):
            for k in xrange(order[2]):
                ti = i * order[1] * order[2] + j * order[2] + k

                # Get the translation vector
                trans[ti] = np.dot([i, j, k], tgens) % 1

                # Get all irreps of that translations
                for l in xrange(ntrans):
                    irreps[l, ti] = np.prod(irrepg[l]**[i, j, k])

    return trans, irreps
Exemple #9
0
def build_translations(tgens):
    """Build a list of translations and irreps from at most 
    three linearly independent generators specified in the 
    form [nx,ny,nz], representing translation [1/nx,1/ny,1/nz].
    """
    if len(tgens) > 3:
        post_error("There can be at most three generators " "of fractional translations.")

    # Check if generators are linearly independent
    if len(tgens) == 2 and np.all(np.cross(tgens[0], tgens[1]) == 0):
        post_error("Generators are not linearly independant.")
    elif len(tgens) == 3 and np.linalg.det(tgens) == 0:
        post_error("Generators are not linearly independant.")

    # Expand the generator list to have a 3x3 matrix
    tgens = np.append(tgens, np.ones((3 - len(tgens), 3)), axis=0)

    # Get the order of every generator
    order = np.array([lcmm(*g) for g in tgens], int)

    # Get the corresponding roots of unity which will be
    # used to construct irreps
    deltag = np.exp(-2 * np.pi * 1j / order)

    # Calculate translation vectors
    tgens = (1.0 / tgens) % 1

    # Total number of translations
    ntrans = np.prod(order)

    # Storage for translations
    trans = np.zeros((ntrans, 3), float)

    # Calculate irreps of every generator
    irrepg = np.zeros((ntrans, 3), complex)

    # Loop over all possible products of powers of generators
    # and store the irreps
    for i in xrange(order[0]):
        for j in xrange(order[1]):
            for k in xrange(order[2]):
                ii = i * order[1] * order[2] + j * order[2] + k

                irrepg[ii] = deltag ** [i, j, k]

    # Irrep table for all translations
    irreps = np.zeros((ntrans, ntrans), complex)

    # Loop over all posible products of powers of generators
    for i in xrange(order[0]):
        for j in xrange(order[1]):
            for k in xrange(order[2]):
                ti = i * order[1] * order[2] + j * order[2] + k

                # Get the translation vector
                trans[ti] = np.dot([i, j, k], tgens) % 1

                # Get all irreps of that translations
                for l in xrange(ntrans):
                    irreps[l, ti] = np.prod(irrepg[l] ** [i, j, k])

    return trans, irreps
Exemple #10
0
def parse_procar(filename):
    '''This function parses a PROCAR file. It returns a tuple
    consisting of following elements:
    
    orbitals    - (norbs) string array of orbital labels (s, px, py etc...)
    kpoints     - (npoints,3) float array of k-point coordinates
    kweights    - (npoints) float array of k-point weights
    bands       - (npoints,nbands,nspin) float array of band energies
    occupancies - (npoints,nbands,nspin) float array of band occupancies
    weights     - (npoints,nions*norbs,nbands,ndim,nspin) float array
                  of orbital weights
    phases      - (npoints,nions*norbs,nbands,nspin) complex array of
                  phases of orbital weights if LORBIT=12, otherwise None
                  
    Where:
    
    norbs   - number of orbitals (It can be 9 or 16 with f orbitals)
    npoints - number of k-points
    nbands  - number of bands
    nspin   - number of spins (1 for non spin-polarized, 2 otherwise)
    nions   - number of atoms
    ndim    - orbital weight dimensionality (1 for collinear, 4 otherwise)
    '''

    try:
        gl = Getlines(filename)
    except:
        post_error('Unable to open "{0}" for reading.'.format(filename))

    header_1 = gl.readline()
    header_2 = gl.readline().split()
    
    npoints = int(header_2[3])
    nbands = int(header_2[7])
    nions = int(header_2[-1])
    
    # Remember the position in file
    start = gl.tell()
    
    # Skip two lines containing first k-point and band
    gl.readline()
    gl.readline()
    
    # Determine the number of orbitals
    orbitals = gl.readline().split()[1:-1]
    
    norbs = len(orbitals)
    
    # Allocate maximal storage. In case calculation was
    # non spin-polarized or collinear we can just trim
    # the excess components at the end
    kpoints = np.zeros((npoints, 3), float)
    kweights = np.zeros(npoints, float)
    bands = np.zeros((npoints, nbands, 2), float)
    occupancies = np.zeros((npoints, nbands, 2), float)
    weights = np.zeros((npoints, nions*norbs, nbands, 4, 2), float)
    
    dim = 0
    
    # Determine if the calculation was non-collinear
    # by counting how many lines in the first band
    # block begin with tot. That number will be equal
    # to the number of sub-blocks for orbital weights
    # (1 in case of collinear and 4 otherwise)
    while True:
        line = gl.readline()
        
        if line.startswith('tot'):
            dim += 1
        elif line.startswith('band'):
            break
    
    # This function will read block of absolute weights
    # for i-th k-point, j-th band and s-th spin component
    def get_absweights(i, j, s):
        # Skip line with orbital names        
        gl.readline()
        
        for k in xrange(dim):
            # Fetch entire orbital weight block
            data = np.fromfile(gl, sep=" ", count=nions*(norbs+2))
            # Cast it into tabular shape
            data = data.reshape((nions, norbs+2))
            
            # Discard first and last columns and store weights
            weights[i,:,j,k,s] = data[:,1:-1].flatten()
            
            # Skip line with the totals
            gl.readline()
            
    # Check whether phase information is included
    if '+ phase' in header_1:
        # Allocate storage for phases
        phases = np.zeros((npoints, nions*norbs, nbands, 2), complex)

        # Declare nested function that handles 
        # parsing of complex weights
        def get_weights(i, j, s):
            # Read abs values of weights
            get_absweights(i, j, s)
            
            # Skip line with orbital names
            gl.readline()
            
            # Fetch entire phase block
            data = np.fromfile(gl, sep=" ", count=2*nions*(norbs+1))
            # Cast it into tabular shape
            data = data.reshape((2*nions, norbs+1))

            # Discard first column and store real and imaginary
            # parts respectively
            phases[i,:,j,s] =  data[::2,1:].flatten()
            phases[i,:,j,s] += 1j*data[1::2,1:].flatten()
    else:
        # Phases are None in this case
        phases = None
        
        # In this case we just have absolutes of weights
        # No need for a new function
        get_weights = get_absweights
    
    # Go back to the beginning of the first k-point
    gl.seek(start)
    
    for i in xrange(npoints):
        # Parse k-point coordinates
        k_line = gl.readline().split()
        
        kpoints[i] = [float(k_line[c]) for c in [3, 4, 5]]
        kweights[i] = float(k_line[-1])
        
        for j in xrange(nbands):
            # Parse band energy
            band_line = gl.readline().split()

            bands[i, j, 0] = float(band_line[4])
            occupancies[i, j, 0] = float(band_line[-1])
            
            # Parse orbital weights
            get_weights(i, j, 0)
    
    # Seek now for the second spin component
    res = gl.readline(False)
    
    # If there is no second spin component, finish by
    # returning just the first component
    if res is None:
        # Trim the second spin component
        bands = bands[:,:,:1]
        occupancies = occupancies[:,:,:1]
        weights = weights[:,:,:,:dim,:1]
        
        if phases is not None:
            phases = phases[:,:,:,:1]
        
        return [orbitals, kpoints, kweights, bands, occupancies, \
            weights, phases]
    
    # Otherwise, read bands and weights for the second component
    for i in xrange(npoints):
        # Skip k-point coordinates
        gl.readline()
        
        for j in xrange(nbands):
            # Parse band energy
            band_line = gl.readline().split()

            bands[i, j, 1] = float(band_line[4])
            occupancies[i, j, 1] = float(band_line[-1])
            
            # Parse orbital weights
            get_weights(i, j, 1)
    
    return [orbitals, kpoints, kweights, bands, occupancies, \
        weights[:,:,:,:dim,:], phases]
Exemple #11
0
def parse_poscar(filename):
    '''Parses POSCAR file. Returns 3x3 float array
    containing unit cell vectors as rows, Nx3
    array containing fractional positions of atoms,
    N being number of atoms and a list of chemical
    symbols.
    '''
    try:
        gl = Getlines(filename)
    except:
        post_error('Unable to open "{0}" for reading.'.format(filename))
    
    # Skip the comment line
    gl.readline()
    
    # Read the scaling factor
    f = float(gl.readline())
    
    # Read the unit cell vectors
    cell = np.zeros((3, 3), float)
    
    cell[0] = f*np.array(gl.readline().split(), float)
    cell[1] = f*np.array(gl.readline().split(), float)
    cell[2] = f*np.array(gl.readline().split(), float)
    
    # Read the chemical symbols
    syms = gl.readline().split()
    
    # Read the atom counts
    counts = np.array(gl.readline().split(), int)
    
    # Get the number of atoms
    natoms = np.sum(counts)
    
    # Rearrange into the long list of chemical symbols
    symbols = []
        
    for s, c in zip(syms, counts):
        symbols += [s]*c
    
    # Cartesian or fractional coordinates?
    ctype = gl.readline()[0].lower()
    
    if ctype == 'c':
        mult = np.linalg.inv(cell)
    elif ctype == 'd':
        mult = np.eye(3)
    else:
        post_error('"{0}" is unknown POSCAR option'.format(plines[7].strip()))
    
    # Allocate storage for positions
    spos = np.zeros((len(symbols), 3))
    
    # Read the positions
    for i in xrange(len(symbols)):
        spos[i] = np.array(gl.readline().split()[:3], float)
    
    # If necessary, this will convert from
    # Cartesian to fractional coordinates
    spos = np.dot(spos, mult)
    
    return cell, spos, symbols
Exemple #12
0
def build_translations(tgens):
    '''Build a list of translations and irreps from at most 
    three linearly independent generators specified as lists
    of three fractions.
    '''
    if len(tgens) > 3:
        post_error('There can be at most three generators '
            'of fractional translations.')
    
    # Use folded generators as floats to perform linear independece tests
    tgensf = np.array(tgens, dtype=float)%1
    
    # Tolerance for linear independenec per dimension
    eps = 1e-2
    
    # Check if generators are linearly independent
    if len(tgens) == 2 and np.all(np.cross(tgensf[0], tgensf[1]) < eps**2):
        post_error('Generators are not linearly independant.')
    elif len(tgens) == 3 and np.linalg.det(np.array(tgensf)) < eps**3:
        post_error('Generators are not linearly independant.')
        
    # Expand the generator list to be a 3x3 matrix
    tgens = np.append(tgens, np.ones((3-len(tgens), 3)), axis=0)
    
    # Get the order of every generator
    order = np.array([frac_translation_order(g) for g in tgens], int)
    
    # Get the corresponding roots of unity which will be
    # used to construct irreps
    deltag = np.exp(-2*np.pi*1j/order)
    
    # Fold the translation vectors into the unit cell
    tgens = tgens % 1
    
    # Total number of translations
    ntrans = np.prod(order)
    
    # Storage for translations
    trans = np.zeros((ntrans, 3), float)
    
    # Calculate irreps of every generator
    irrepg = np.zeros((ntrans, 3), complex)
    
    # Loop over all possible products of powers of generators
    # and store the irreps
    for i in xrange(order[0]):
        for j in xrange(order[1]):
            for k in xrange(order[2]):
                ii = i*order[1]*order[2]+j*order[2]+k
                
                irrepg[ii] = deltag**[i, j, k]
    
    # Irrep table for all translations          
    irreps = np.zeros((ntrans, ntrans), complex)
    
    # Loop over all posible products of powers of generators
    for i in xrange(order[0]):
        for j in xrange(order[1]):
            for k in xrange(order[2]):
                ti = i*order[1]*order[2]+j*order[2]+k
                
                # Get the translation vector
                trans[ti] = np.dot([i, j, k], tgens)%1
                
                # Get all irreps of that translations
                for l in xrange(ntrans):
                    irreps[l, ti] = np.prod(irrepg[l]**[i, j, k])
                    
    return trans, irreps
def build_translations(tgens):
    '''Build a list of translations and irreps from at most 
    three linearly independent generators specified in the 
    form [nx,ny,nz], representing translation [1/nx,1/ny,1/nz].
    '''
    if len(tgens) > 3:
        post_error('There can be at most three generators '
                   'of fractional translations.')

    # Check if generators are linearly independent
    if len(tgens) == 2 and np.all(np.cross(tgens[0], tgens[1]) == 0):
        post_error('Generators are not linearly independant.')
    elif len(tgens) == 3 and np.linalg.det(tgens) == 0:
        post_error('Generators are not linearly independant.')

    # Expand the generator list to have a 3x3 matrix
    tgens = np.append(tgens, np.ones((3 - len(tgens), 3)), axis=0)

    # Get the order of every generator
    order = np.array([lcmm(*g) for g in tgens], int)

    # Get the corresponding roots of unity which will be
    # used to construct irreps
    deltag = np.exp(-2 * np.pi * 1j / order)

    # Calculate translation vectors
    tgens = (1.0 / tgens) % 1

    # Total number of translations
    ntrans = np.prod(order)

    # Storage for translations
    trans = np.zeros((ntrans, 3), float)

    # Calculate irreps of every generator
    irrepg = np.zeros((ntrans, 3), complex)

    # Loop over all possible products of powers of generators
    # and store the irreps
    for i in xrange(order[0]):
        for j in xrange(order[1]):
            for k in xrange(order[2]):
                ii = i * order[1] * order[2] + j * order[2] + k

                irrepg[ii] = deltag**[i, j, k]

    # Irrep table for all translations
    irreps = np.zeros((ntrans, ntrans), complex)

    # Loop over all posible products of powers of generators
    for i in xrange(order[0]):
        for j in xrange(order[1]):
            for k in xrange(order[2]):
                ti = i * order[1] * order[2] + j * order[2] + k

                # Get the translation vector
                trans[ti] = np.dot([i, j, k], tgens) % 1

                # Get all irreps of that translations
                for l in xrange(ntrans):
                    irreps[l, ti] = np.prod(irrepg[l]**[i, j, k])

    return trans, irreps
Exemple #14
0
def parse_procar(filename):
    '''This function parses a PROCAR file. It returns a tuple
    consisting of following elements:
    
    orbitals    - (norbs) string array of orbital labels (s, px, py etc...)
    kpoints     - (npoints,3) float array of k-point coordinates
    kweights    - (npoints) float array of k-point weights
    bands       - (npoints,nbands,nspin) float array of band energies
    occupancies - (npoints,nbands,nspin) float array of band occupancies
    weights     - (npoints,nions*norbs,nbands,ndim,nspin) float array
                  of orbital weights
    phases      - (npoints,nions*norbs,nbands,nspin) complex array of
                  phases of orbital weights if LORBIT=12, otherwise None
                  
    Where:
    
    norbs   - number of orbitals (It can be 9 or 16 with f orbitals)
    npoints - number of k-points
    nbands  - number of bands
    nspin   - number of spins (1 for non spin-polarized, 2 otherwise)
    nions   - number of atoms
    ndim    - orbital weight dimensionality (1 for collinear, 4 otherwise)
    '''

    try:
        gl = Getlines(filename)
    except:
        post_error('Unable to open "{0}" for reading.'.format(filename))

    header_1 = gl.readline()
    header_2 = gl.readline().split()

    npoints = int(header_2[3])
    nbands = int(header_2[7])
    nions = int(header_2[-1])

    # Remember the position in file
    start = gl.tell()

    # Skip two lines containing first k-point and band
    gl.readline()
    gl.readline()

    # Determine the number of orbitals
    orbitals = gl.readline().split()[1:-1]

    norbs = len(orbitals)

    # Allocate maximal storage. In case calculation was
    # non spin-polarized or collinear we can just trim
    # the excess components at the end
    kpoints = np.zeros((npoints, 3), float)
    kweights = np.zeros(npoints, float)
    bands = np.zeros((npoints, nbands, 2), float)
    occupancies = np.zeros((npoints, nbands, 2), float)
    weights = np.zeros((npoints, nions * norbs, nbands, 4, 2), float)

    dim = 0

    # Determine if the calculation was non-collinear
    # by counting how many lines in the first band
    # block begin with tot. That number will be equal
    # to the number of sub-blocks for orbital weights
    # (1 in case of collinear and 4 otherwise)
    while True:
        line = gl.readline()

        if line.startswith('tot'):
            dim += 1
        elif line.startswith('band'):
            break

    # This function will read block of absolute weights
    # for i-th k-point, j-th band and s-th spin component
    def get_absweights(i, j, s):
        # Skip line with orbital names
        gl.readline()

        for k in xrange(dim):
            # Fetch entire orbital weight block
            data = np.fromfile(gl, sep=" ", count=nions * (norbs + 2))
            # Cast it into tabular shape
            data = data.reshape((nions, norbs + 2))

            # Discard first and last columns and store weights
            weights[i, :, j, k, s] = data[:, 1:-1].flatten()

            # Skip line with the totals
            gl.readline()

    # Check whether phase information is included
    if '+ phase' in header_1:
        # Allocate storage for phases
        phases = np.zeros((npoints, nions * norbs, nbands, 2), complex)

        # Declare nested function that handles
        # parsing of complex weights
        def get_weights(i, j, s):
            # Read abs values of weights
            get_absweights(i, j, s)

            # Skip line with orbital names
            gl.readline()

            # Fetch entire phase block
            data = np.fromfile(gl, sep=" ", count=2 * nions * (norbs + 1))
            # Cast it into tabular shape
            data = data.reshape((2 * nions, norbs + 1))

            # Discard first column and store real and imaginary
            # parts respectively
            phases[i, :, j, s] = data[::2, 1:].flatten()
            phases[i, :, j, s] += 1j * data[1::2, 1:].flatten()
    else:
        # Phases are None in this case
        phases = None

        # In this case we just have absolutes of weights
        # No need for a new function
        get_weights = get_absweights

    # Go back to the beginning of the first k-point
    gl.seek(start)

    for i in xrange(npoints):
        # Parse k-point coordinates
        k_line = gl.readline().split()

        kpoints[i] = [float(k_line[c]) for c in [3, 4, 5]]
        kweights[i] = float(k_line[-1])

        for j in xrange(nbands):
            # Parse band energy
            band_line = gl.readline().split()

            bands[i, j, 0] = float(band_line[4])
            occupancies[i, j, 0] = float(band_line[-1])

            # Parse orbital weights
            get_weights(i, j, 0)

    # Seek now for the second spin component
    res = gl.readline(False)

    # If there is no second spin component, finish by
    # returning just the first component
    if res is None:
        # Trim the second spin component
        bands = bands[:, :, :1]
        occupancies = occupancies[:, :, :1]
        weights = weights[:, :, :, :dim, :1]

        if phases is not None:
            phases = phases[:, :, :, :1]

        return [orbitals, kpoints, kweights, bands, occupancies, \
            weights, phases]

    # Otherwise, read bands and weights for the second component
    for i in xrange(npoints):
        # Skip k-point coordinates
        gl.readline()

        for j in xrange(nbands):
            # Parse band energy
            band_line = gl.readline().split()

            bands[i, j, 1] = float(band_line[4])
            occupancies[i, j, 1] = float(band_line[-1])

            # Parse orbital weights
            get_weights(i, j, 1)

    return [orbitals, kpoints, kweights, bands, occupancies, \
        weights[:,:,:,:dim,:], phases]