Example #1
0
def cif2structure(filename, primitive=False, symprec=0.001):
    """
    Read a given file  as a cif file, convert it into a
    ase atoms object and finally into a pychemia
    structure
    """
    aseatoms = ase.io.read(filename)
    if primitive:
        #lattice, scaled_positions, numbers = spglib.refine_cell(aseatoms, symprec)
        #ref_atoms=ase.atoms(cell=lattice,scaled_positions=scaled_positions,numbers=numbers)
        lattice, scaled_positions, numbers = spglib.find_primitive(aseatoms, symprec)
        if lattice is not None and scaled_positions is not None and numbers is not None:
            fin_atoms = ase.atoms.Atoms(cell=lattice, scaled_positions=scaled_positions, numbers=numbers, pbc=True)
        else:
            fin_atoms = aseatoms
    else:
        fin_atoms = aseatoms
    ret = ase2pychemia(fin_atoms)
    return ret
Example #2
0
def spglib_get_primitive(struct, **kwds):
    """Find primitive structure for given :class:`~pwtools.crys.Structure`.

    If `struct` is irreducible (is already a primitive cell), we return None,
    else a Structure.

    Uses pyspglib.

    Parameters
    ----------
    struct : Structure
    **kwds : keywords
        passed to ``spglib.find_primitive()``, e.g. `symprec` and
        `angle_tolerance` last time I checked

    Returns
    -------
    Structure or None

    Notes
    -----
    spglib used to return (None,None,None) if no primitive cell can be found,
    i.e. the given input Structure cannot be reduced, which can occur if (a) a
    given Structure is already a primitive cell or (b) any other reason like a
    too small value of `symprec`. Now [py]spglib >= 1.8.x seems to always
    return data instead. We use :func:`is_same_struct` to determine if the
    struct is irreducible. In that case we return None in order to keep the API
    unchanged.

    Also note that a primitive cell (e.g. with 2 atoms) can have a number of
    different realizations. Therefore, you may not always get the primitive
    cell which you would expect or get from other tools like Wien2K's sgroup.
    Only things like `natoms` and the spacegroup can be safely compared.
    """
    candidate = spglib2struct(spglib.find_primitive(struct.get_fake_ase_atoms(),
                                                    **kwds))
    if is_same_struct(candidate, struct):
        return None
    else:
        return candidate
Example #3
0
def analyze_QHA_run(Calc, Tmin=1, Tmax=1500, Tsteps=100):
    '''
    Run the QHA analysis procedure on the set of volume calculation Calc.
    This procedure produces an array with thermodynamic functions for
    all calculated volumes and given temperature range (the zero temperature is always included).
    
    Input
    =====
    
        Calc   - List of calculation data
        Tmin   - Minimum temperature of the scan (K)
        Tmax   - Maximum temperature of the scan (K)
        Tsteps - Number of temperature steps
        bdir   - base directory (subdirectories host and calculation are located below)
        
    Output
    ======
        
        qha  - array of N x Tsteps x #params containing resulting data:
               V, T, Etot, Fph, P*V, Cv, S, ... 
               for each calculation and each temperature step.
    '''
    phdos={}
    qha=[]
    
    for n,c in enumerate(Calc):
        PCMUL=len(c.get_atomic_numbers())/len(spglib.find_primitive(c)[2])
        # Read the calculated dos
        clc=c.calc.results
        phdos[n]=clc['phdos']
        #Etot=c.get_potential_energy()
        Etot=clc['etotal']*Rydberg

        # Pressure returned in kbar we need to change it to eV/A^3 to get p*V in eV
        # eV/A^3=160.2176487GPa
        # kbar=0.1 GPa
        # eV/A^3 = 1602.176487 kbar
        P=clc['pressure']/1602.176487
        #P=c.get_isotropic_pressure(c.get_stress())

        # Scan the temperatures at this volume 
        # Unit cell (primitive) volume in Bohr^3 => (A^3)
        #V=(calcQHA[idx][2]['A'])**3
        
        #V=clc['volume']*(Bohr**3)
        V=c.get_volume()/PCMUL
    
        # get the ndf from the normalization of dos
        dos=phdos[n]
        ndf=round(simps(dos[1],dos[0]))
        
        # frequencies/energies
        # QE outputs dos in cm^-1, let's convert to sane units 
        # Let's convert to energy to include the hbar
        nu=1e-3*cminv2meV*phdos[n][0]
        dos=phdos[n][1]
        
        # We need to cut the nu to positive range
        no=array([[o,g] for o,g in zip(nu,dos) if o>0 ])
        nu=no[:,0]
        dos=no[:,1]
    
        # correct the normalization for units, negative cut and numerical errors
        dos=dos/simps(dos,nu)
    
        # plot in meV
        #plot(1e-3*nu,dos,label=n);
        #legend()
        #show()
        
        # plot the integrand at 300K
        #T=300
        #plot(nu,dos*log(2*sinh(0.5*nu/(k_B*T))),label=idx)
        
        # Zero-point energy - $\hbar\omega/2$ for each degree of freedom integrated over $\omega$
        F0=ndf*simps(0.5*dos*nu,nu)
    
        # Put in special case for the T=0. Just ZPV energy.
        qhav=[[V,0,Etot,F0,0,0,0,0]]
        qha.append(qhav)
        print '# %s: V=%.2f A^3, Etot=%.3f eV, P=%6.1f GPa' % (n, V, Etot, P/GPa)
        for T in linspace(Tmin,Tmax,Tsteps):
            # Maradudin book formula - the only one important for thermal expansion
            # The result is in eV, temperature in K
            e=0.5*nu/(k_B*T)
            Fph=ndf*T*k_B*simps(dos*log(2*sinh(e)),nu)
            Cv=ndf*k_B*simps(e*e*dos/sinh(e)**2,nu)
            S=k_B*(simps(2*dos*e/(exp(2*e)-1),nu)-simps(dos*(1-exp(-2*e)),nu))
            # Alternative formula from QE
            Sqe=k_B*simps(dos*(e/tanh(e)-log(2*sinh(e))),nu)
            # Formulas lifted from the QHA code in QE - not correct with current units
            #q=0.5*a3*nu/T
            #E_int=simps(a1*dos*nu/tanh(q),nu)
            #S0=simps(dos*(q/tanh(q)-log(2*sinh(q))),nu)
            qhav.append([V, T, Etot, Fph, P*V, Cv, S, Sqe])
            #print " %5.2f  %12f  %12f  %12f" % (T, Cv, S, Sqe)
        
    return array(qha)
Example #4
0
def get_periodic_symmetry_operations(atoms, error_tolerance=0.01, debug=False):
    from ase.utils import irotate
    from ase.visualize import view
    from pyspglib import spglib
    #symmetry = spglib.get_symmetry(atoms, symprec=1e-5)
    symmetry = spglib.get_symmetry(atoms, symprec=1e-2)
    dataset = spglib.get_symmetry_dataset(atoms, symprec=1e-2)

    result = []
    if debug:
        cell, scaled_positions, numbers = spglib.find_primitive(atoms,
                                                                symprec=1e-5)
        a = Atoms(symbols='O4',
                  cell=cell,
                  scaled_positions=scaled_positions,
                  pbc=True)
        #symmetry = spglib.get_symmetry(a, symprec=1e-2)
        #dataset = spglib.get_symmetry_dataset(a, symprec=1e-2)
        print dataset['equivalent_atoms']
        print dataset['international']
        print dataset['hall']
        print dataset['wyckoffs']
        print dataset['transformation_matrix']
        print "Number of symmetry operations %i" % len(dataset['rotations'])
    for i in range(dataset['rotations'].shape[0]):

        new_atoms = atoms.copy()
        test = atoms.copy()
        rot = dataset['rotations'][i]
        trans = dataset['translations'][i]
        if debug:
            x, y, z = irotate(rot)
            #print x, y, z
            print "------------------- %i -----------------------" % i
            print "Rotation"
            print rot
            print "Translation"
            print trans
        new_pos = new_atoms.get_scaled_positions()

        for l, pos in enumerate(new_atoms.get_scaled_positions()):
            #print new_pos[l]
            new_pos[l] = (numpy.dot(rot, pos))
            new_pos[l] += trans
            #print new_pos[l]
        new_atoms.set_scaled_positions(new_pos)
        equals = get_equals_periodic(atoms,
                                     new_atoms,
                                     error_tolerance=error_tolerance,
                                     debug=debug)
        if equals != None:
            so = SymmetryOperation(str(i),
                                   equals,
                                   None,
                                   vector=None,
                                   magnitude=1,
                                   rotation_matrix=rot,
                                   translation_vector=trans)
            #if debug:
            #    print so
            result.append(so)
        else:
            print "Equivalent not found"
            #view(test)
            #view(new_atoms)
            #raw_input()

    return result
Example #5
0
def write_cell_params(fh, a, p):
    '''
    Write the specification of the cell using a traditional A,B,C, 
    alpha, beta, gamma scheme. The symmetry and parameters are determined
    from the atoms (a) object. The atoms object is translated into a 
    primitive unit cell but *not* converted. This is just an internal procedure.
    If you wish to work with primitive unit cells in ASE, you need to make 
    a conversion yourself. The cell params are in angstrom.
    
    Input
    -----
        fh  file handle of the opened pw.in file
        a   atoms object
        p   parameters dictionary
        
    Output
    ------
        Primitive cell tuple of arrays: (lattice, atoms, atomic numbers)
    '''
    
    assert(p['use_symmetry'])
    
    # Get a spacegroup name and number for the a
    sg,sgn=spglib.get_spacegroup(a).split()
    # Extract the number
    sgn=int(sgn[1:-1])
    # Extract the lattice type
    ltyp=sg[0]
    
    # Find a primitive unit cell for the system
    # puc is a tuple of (lattice, atoms, atomic numbers)
    puc=spglib.find_primitive(a)
    cell=puc[0]
    apos=puc[1]
    anum=puc[2]
    icell=a.get_cell()
    A=norm(icell[0])
    B=norm(icell[1])
    C=norm(icell[2])

    # Select appropriate ibrav
    if sgn >= 195 :
        # Cubic lattice
        if   ltyp=='P':  
            p['ibrav']=1  # Primitive
            qepc=array([[1,0,0],[0,1,0],[0,0,1]])
        elif ltyp=='F':  
            p['ibrav']=2  # FCC
            qepc=array([[-1,0,1],[0,1,1],[-1,1,0]])/2.0
        elif ltyp=='I':  
            p['ibrav']=3  # BCC
            qepc=array([[1,1,1],[-1,1,1],[-1,-1,1]])/2.0
        else :
            print 'Impossible lattice symmetry! Contact the author!'
            raise NotImplementedError
        #a=sqrt(2)*norm(cell[0])
        qepc=A*qepc
        fh.write('      A = %f,\n' % (A,))
    elif sgn >= 143 :
        # Hexagonal and trigonal 
        if   ltyp=='P' :  
            p['ibrav']=4  # Primitive
            qepc=array([[1,0,0],[-1/2,sqrt(3)/2,0],[0,0,C/A]])
        elif ltyp=='R' :  
            p['ibrav']=5  # Trigonal rhombohedral
            raise NotImplementedError
        else :
            print 'Impossible lattice symmetry! Contact the author!'
            raise NotImplementedError
        qepc=A*qepc
        fh.write('      A = %f,\n' % (A,))
        fh.write('      C = %f,\n' % (C,))
    elif sgn >= 75 :
        raise NotImplementedError
    elif sgn ==1 :
        # P1 symmetry - no special primitive cell signal to the caller
        p['ibrav']=0
        return None
    else :
        raise NotImplementedError
    cp=Atoms(cell=puc[0], scaled_positions=puc[1], numbers=puc[2], pbc=True)
    qepc=Atoms(cell=qepc, 
        positions=cp.get_positions(), 
        numbers=cp.get_atomic_numbers(), pbc=True)
    return qepc.get_cell(), qepc.get_scaled_positions(), qepc.get_atomic_numbers()
Example #6
0
def analyze_QHA_run(Calc, Tmin=1, Tmax=1500, Tsteps=100):
    '''
    Run the QHA analysis procedure on the set of volume calculation Calc.
    This procedure produces an array with thermodynamic functions for
    all calculated volumes and given temperature range (the zero temperature is always included).
    
    Input
    =====
    
        Calc   - List of calculation data
        Tmin   - Minimum temperature of the scan (K)
        Tmax   - Maximum temperature of the scan (K)
        Tsteps - Number of temperature steps
        bdir   - base directory (subdirectories host and calculation are located below)
        
    Output
    ======
        
        qha  - array of N x Tsteps x #params containing resulting data:
               V, T, Etot, Fph, P*V, Cv, S, ... 
               for each calculation and each temperature step.
    '''
    phdos = {}
    qha = []

    for n, c in enumerate(Calc):
        PCMUL = len(c.get_atomic_numbers()) / len(spglib.find_primitive(c)[2])
        # Read the calculated dos
        clc = c.calc.results
        phdos[n] = clc['phdos']
        #Etot=c.get_potential_energy()
        Etot = clc['etotal'] * Rydberg

        # Pressure returned in kbar we need to change it to eV/A^3 to get p*V in eV
        # eV/A^3=160.2176487GPa
        # kbar=0.1 GPa
        # eV/A^3 = 1602.176487 kbar
        P = clc['pressure'] / 1602.176487
        #P=c.get_isotropic_pressure(c.get_stress())

        # Scan the temperatures at this volume
        # Unit cell (primitive) volume in Bohr^3 => (A^3)
        #V=(calcQHA[idx][2]['A'])**3

        #V=clc['volume']*(Bohr**3)
        V = c.get_volume() / PCMUL

        # get the ndf from the normalization of dos
        dos = phdos[n]
        ndf = round(simps(dos[1], dos[0]))

        # frequencies/energies
        # QE outputs dos in cm^-1, let's convert to sane units
        # Let's convert to energy to include the hbar
        nu = 1e-3 * cminv2meV * phdos[n][0]
        dos = phdos[n][1]

        # We need to cut the nu to positive range
        no = array([[o, g] for o, g in zip(nu, dos) if o > 0])
        nu = no[:, 0]
        dos = no[:, 1]

        # correct the normalization for units, negative cut and numerical errors
        dos = dos / simps(dos, nu)

        # plot in meV
        #plot(1e-3*nu,dos,label=n);
        #legend()
        #show()

        # plot the integrand at 300K
        #T=300
        #plot(nu,dos*log(2*sinh(0.5*nu/(k_B*T))),label=idx)

        # Zero-point energy - $\hbar\omega/2$ for each degree of freedom integrated over $\omega$
        F0 = ndf * simps(0.5 * dos * nu, nu)

        # Put in special case for the T=0. Just ZPV energy.
        qhav = [[V, 0, Etot, F0, 0, 0, 0, 0]]
        qha.append(qhav)
        print '# %s: V=%.2f A^3, Etot=%.3f eV, P=%6.1f GPa' % (n, V, Etot,
                                                               P / GPa)
        for T in linspace(Tmin, Tmax, Tsteps):
            # Maradudin book formula - the only one important for thermal expansion
            # The result is in eV, temperature in K
            e = 0.5 * nu / (k_B * T)
            Fph = ndf * T * k_B * simps(dos * log(2 * sinh(e)), nu)
            Cv = ndf * k_B * simps(e * e * dos / sinh(e)**2, nu)
            S = k_B * (simps(2 * dos * e / (exp(2 * e) - 1), nu) -
                       simps(dos * (1 - exp(-2 * e)), nu))
            # Alternative formula from QE
            Sqe = k_B * simps(dos * (e / tanh(e) - log(2 * sinh(e))), nu)
            # Formulas lifted from the QHA code in QE - not correct with current units
            #q=0.5*a3*nu/T
            #E_int=simps(a1*dos*nu/tanh(q),nu)
            #S0=simps(dos*(q/tanh(q)-log(2*sinh(q))),nu)
            qhav.append([V, T, Etot, Fph, P * V, Cv, S, Sqe])
            #print " %5.2f  %12f  %12f  %12f" % (T, Cv, S, Sqe)

    return array(qha)