Ejemplo n.º 1
0
 def getLatticeParameters(self):
     """Extract lattice parameters after pwscf geometry optimization
        Returns a list of 6 parameters: A, B, C, cos(BC), cos(AC), cos(AB)"""
     from qelattice import QELattice
     # obtain lattice from PWSCF input file:
     lat = QELattice(fname=self.pwscfInput)
     pwscfOut = read_file(self.pwscfOutput)
     key_a_0 = find_key_from_string(pwscfOut, 'lattice parameter (a_0)')
     a_0 = float(string.split(pwscfOut[key_a_0])[4])
     if lat.type == 'traditional':
         a_0 = a_0 * 0.529177249  # convert back to angstrom
     keyEnd = max(
         find_all_keys_from_marker_string(pwscfOut, '!', 'total energy'))
     keyCellPar = find_key_from_string_afterkey(pwscfOut, keyEnd, \
                                               'CELL_PARAMETERS (alat)') + 1
     latticeVectors = [
         [
             float(valstr) * a_0
             for valstr in string.split(pwscfOut[keyCellPar])
         ],
         [
             float(valstr) * a_0
             for valstr in string.split(pwscfOut[keyCellPar + 1])
         ],
         [
             float(valstr) * a_0
             for valstr in string.split(pwscfOut[keyCellPar + 2])
         ]
     ]
     lat.setLatticeFromQEVectors(lat.ibrav, latticeVectors)
     return [lat.a, lat.b, lat.c, lat.cBC, lat.cAC, lat.cAB]
Ejemplo n.º 2
0
 def reduce(self, ibrav = None): 
     """
     Reduces a structure instance with nonprimitive lattice ( i.e. solves
     for equivalent atomic positions) according to specified  lattice type (ibrav). 
     Each ibrav number corresponds to a specific primitive lattice 
     (see QE documentation).         
     """
     ib = self.lattice.ibrav
     if ibrav != None:
         ib = ibrav            
     a = self.lattice.matter().a
     b = self.lattice.matter().b
     c = self.lattice.matter().c
     cAB = cosd(self.lattice.matter().gamma)
     cBC = cosd(self.lattice.matter().alpha)
     cAC = cosd(self.lattice.matter().beta)
     if ib > 0:
         qeLattice = QELattice(ibrav = ib, a = a, b = b, c = c,  cBC =  cBC, \
                           cAC = cAC, cAB = cAB)
     else:
         qeLattice = QELattice(ibrav = ib, base = self.lattice.base)            
     qeLattice._qeInput = self._qeInput
     reducedStructure = QEStructure(self)
     reducedStructure.placeInLattice( qeLattice )
     # collect atoms that are at equivalent position to some previous atom
     duplicates = set([a1
         for i0, a0 in enumerate(reducedStructure) for a1 in reducedStructure[i0+1:]
             if  a0.symbol == a1.symbol and equalPositions(a0.xyz, a1.xyz, eps=1e-4)])
     # Filter out duplicate atoms.  Use slice assignment so that
     # reducedStructure is not replaced with a list.
     self.lattice = qeLattice
     self[:] = [a for a in reducedStructure if not a in duplicates]        
     self._qeInput.structure = self
     return
Ejemplo n.º 3
0
 def _setStructureFromMatterStructure(self, structure, massList = [], psList = [], ibrav = 0):
     """
     structure - matter.Structure object
     ibrav - Lattice index
     psList - list of strings with potential names
     matterStructure object will be modified with reduced atomic positions
     """      
     matterLattice = structure.lattice
     qeLattice = QELattice(ibrav = 0, base = matterLattice.base)
     qeLattice.a = 1.889725989*qeLattice.a
     qeLattice._qeInput = self._qeInput
     self.lattice = qeLattice
     self.lattice.type = 'generic cubic'
     atomNames = []        
     for a in structure:
         if a.symbol not in atomNames:
             atomNames.append(a.symbol)
     atomicSpecies = {}
     for i, elem in enumerate(atomNames):
         if len(massList) - 1 < i:
             mass = 0
         else:
             mass = massList[i]
         if len(psList) - 1 < i:
             ps = ''
         else:
             ps = psList[i]               
         atomicSpecies[elem] =  (mass, ps)   
     self[:] = []
     for atom in structure:
         self.addNewAtom(atype = atom.symbol, xyz = atom.xyz, \
                         mass = atomicSpecies[atom.symbol][0], \
                         potential = atomicSpecies[atom.symbol][1],\
                         lattice = self.lattice, optConstraint = [])  
Ejemplo n.º 4
0
    def __init__(self, atoms = [], lattice = None, filename = None, qeInput = None):
        """
        atoms        -- list of QEAtom atom instances or a QEStructure object
        lattice      -- QELattice object
        filename     -- filename QE input file 
        qeInput      -- pointer to a PWInput parsing object. If not None, 
                        its PWInput.structure and PWInput.structure.lattice 
                        will be  reset to the current instance of the structure       
        """
          
        Structure.__init__(self) 
        self.formatString = '%# .8f %# .8f %# .8f'
        self._atomicPositionsType = 'crystal'
        self._qeInput = qeInput
        self.lattice = QELattice()
        self.lattice._qeInput = self._qeInput
           
        if lattice != None:

            if self.lattice._qeInput != None:            
                self._qeInput = self.lattice._qeInput
            else:
               self.lattice._qeInput = self._qeInput              
                       
            self.lattice = QELattice( lattice = lattice )
                      
           
        # CP and PW inputs are compatible
        from pwinput import PWInput
        from cpinput import CPInput        
        if isinstance( atoms, PWInput) or isinstance( atoms, CPInput):
            qeInput = atoms        
        elif isinstance( atoms, QEStructure):
            stru = atoms                
            self.__dict__.update( stru.__dict__ )            
            # deep copy of the lattice will deep copy PWInput as well
            self.lattice = QELattice(lattice = stru.lattice)            
            self._qeInput = self.lattice._qeInput                
            self[:] = stru
        else:
            if self.lattice == None:
                raise "Lattice must be provided"                            
            self[:] = atoms
        
        if filename != None:        
            qeInput = PWInput(filename = filename)
            qeInput.parse()
            self.parseInput(qeInput)            
        
        if qeInput != None:            
            self.lattice._qeInput = qeInput
            self._qeInput = qeInput
        
        if self.lattice._qeInput != None:
            self.lattice._qeInput.structure = self
            self.lattice._qeInput.structure.lattice = self.lattice
            
        return
Ejemplo n.º 5
0
 def _setReducedStructureFromMatterStructure(self, structure, ibrav, massList = [], psList = []):
     """
     structure - matter.Structure object
     ibrav - Lattice index
     psList - list of strings with potential names
     matterStructure object will be modified with reduced atomic positions
     """
     import copy
     matterLattice = copy.deepcopy(structure.lattice)
     # there is a big problem with what Nikolay is doing because he is initializing QELattice with abcalbega which
     # leaves room for errors in how the basis vectors are chosen compared to the old lattice
     a = matterLattice.a
     b = matterLattice.b
     c = matterLattice.c
     cAB = cosd(matterLattice.gamma)
     cBC = cosd(matterLattice.alpha)
     cAC = cosd(matterLattice.beta)
     qeLattice = QELattice(ibrav = ibrav, a = a, b = b, c = c,  cBC =  cBC, \
                           cAC = cAC, cAB = cAB)
     #what he should do is initialize the vectors of QELattice with the vectors of matterLattice
     #
     qeLattice._qeInput = self._qeInput
     self.lattice = qeLattice
     # make a deep copy:
     reducedStructure = Structure(atoms = structure)
     reducedStructure.placeInLattice(Lattice(base=qeLattice.matter().base))
     # collect atoms that are at equivalent position to some previous atom
     duplicates = set([a1
         for i0, a0 in enumerate(reducedStructure) for a1 in reducedStructure[i0+1:]
             if a0.symbol==a1.symbol and equalPositions(a0.xyz, a1.xyz, eps=1e-4)])
     # Filter out duplicate atoms.  Use slice assignment so that
     # reducedStructure is not replaced with a list.
     reducedStructure[:] = [a for a in reducedStructure if not a in duplicates]
     atomNames = []
     for a in reducedStructure:
         if a.symbol not in atomNames:
             atomNames.append(a.symbol)
     atomicSpecies = {}
     for i, elem in enumerate(atomNames):
         if len(massList) - 1 < i:
             mass = 0
         else:
             mass = massList[i]
         if len(psList) - 1 < i:
             ps = ''
         else:
             ps = psList[i]
         atomicSpecies[elem] =  (mass, ps)
     self[:] = []
     # convert to bohr units
     self.lattice.setLattice(ibrav, self.lattice.a*1.889725989, \
                              self.lattice.b*1.889725989,
                              self.lattice.c*1.889725989)
     for atom in reducedStructure:
         self.addNewAtom(atype = atom.symbol, xyz = atom.xyz, \
                         mass = atomicSpecies[atom.symbol][0], \
                         potential = atomicSpecies[atom.symbol][1],\
                         lattice = self.lattice, optConstraint = [])
Ejemplo n.º 6
0
 def __init__(self, qeConf):
     """the structure is initialized from PWSCF config file
        'lattice' and 'structure' are automatically updated"""
     self.filename = qeConf.filename
     self.atomicSpecies = OrderedDict()
     self.formatString = '%# .8f %# .8f %# .8f'
     # optConstraints three 1/0 for each coordinate of each atom
     self.optConstraints = []
     self.qeConf = qeConf
     self.lattice = QELattice()
     self.structure = Structure(lattice=self.lattice.diffpy())
     self.nat = None
     self.ntyp = None
Ejemplo n.º 7
0
    def setStructureFromDiffpyStructure(self, structure, massList = [], psList = [], ibrav = 0):
        """
        structure - diffpy.Structure object
        ibrav - Lattice index
        psList - list of strings with pseudopotential names
        diffpyStructure object will be modified with reduced atomic positions
        """      
        diffpyLattice = structure.lattice
        
        self.structure = structure
               
        
        #set lattice and  convert to bohr units
        qeLattice = QELattice(ibrav = 0, a = 1.889725989, base = diffpyLattice.base)
        
        
        self.lattice = qeLattice
        self.lattice.type = 'generic cubic'

        atomNames = []
        for a in structure:
            if self._element(a) not in atomNames:
                atomNames.append(self._element(a))
        
        #print atomNames
        #print len(massList)
        for i, elem in enumerate(atomNames):
            if len(massList) - 1 < i:
                mass = 0
            else:
                mass = massList[i]
            if len(psList) - 1 < i:
                ps = ''
            else:
                ps = psList[i]               
            self.atomicSpecies[elem] =  AtomicSpecies(elem, mass, ps)
        
        for atom in structure:
            self.optConstraints.append([])        
        
        # for i, atom in enumerate(structure):
            # elem = self._element(atom)
            # if len(massList) - 1 < i:
                # mass = 0
            # else:
                # mass = massList[i]
            # if len(psList) - 1 < i:
                # ps = ''
            # else:
                # ps = psList[i]
            # self.atomicSpecies[elem] =  AtomicSpecies(elem, mass, ps)
            # self.optConstraints.append([])

#        for atom, mass, ps in zip(structure, massList, psList):
#            elem = self._element(atom)
#            self.atomicSpecies[elem] =  AtomicSpecies(elem, mass, ps)
#            self.optConstraints.append([])

        self.nat = len(structure)
        self.ntyp = len(self.atomicSpecies)        
Ejemplo n.º 8
0
 def getLatticeParameters(self):
     """Extract lattice parameters after pwscf geometry optimization
        Returns a list of 6 parameters: A, B, C, cos(BC), cos(AC), cos(AB)"""
     from qelattice import QELattice
     # obtain lattice from PWSCF input file:
     lat = QELattice(fname = self.pwscfInput)
     pwscfOut = read_file(self.pwscfOutput)
     key_a_0 = find_key_from_string(pwscfOut, 'lattice parameter (a_0)')
     a_0 = float( string.split( pwscfOut[key_a_0] )[4] )
     if lat.type == 'traditional': a_0 = a_0*0.529177249 # convert back to angstrom
     keyEnd = max( find_all_keys_from_marker_string(pwscfOut, '!', 'total energy') )
     keyCellPar = find_key_from_string_afterkey(pwscfOut, keyEnd, \
                                               'CELL_PARAMETERS (alat)') + 1
     latticeVectors = [[float(valstr)*a_0 for valstr in string.split( pwscfOut[keyCellPar] ) ],
                      [ float(valstr)*a_0 for valstr in string.split( pwscfOut[keyCellPar+1] ) ],
                      [ float(valstr)*a_0 for valstr in string.split( pwscfOut[keyCellPar+2] ) ]]
     lat.setLatticeFromQEVectors(lat.ibrav, latticeVectors)
     return [lat.a, lat.b, lat.c, lat.cBC ,lat.cAC , lat.cAB]
Ejemplo n.º 9
0
    def reduce(self, ibrav=None):
        """
        Reduces a structure instance with nonprimitive lattice ( i.e. solves
        for equivalent atomic positions) according to specified  lattice type (ibrav). 
        Each ibrav number corresponds to a specific primitive lattice 
        (see QE documentation).         
        """

        ib = self.lattice.ibrav
        if ibrav != None:
            ib = ibrav

        a = self.lattice.diffpy().a
        b = self.lattice.diffpy().b
        c = self.lattice.diffpy().c
        cAB = cosd(self.lattice.diffpy().gamma)
        cBC = cosd(self.lattice.diffpy().alpha)
        cAC = cosd(self.lattice.diffpy().beta)
        if ib > 0:
            qeLattice = QELattice(ibrav = ib, a = a, b = b, c = c,  cBC =  cBC, \
                              cAC = cAC, cAB = cAB)
        else:
            qeLattice = QELattice(ibrav=ib, base=self.lattice.base)

        qeLattice._qeInput = self._qeInput

        reducedStructure = QEStructure(self)

        reducedStructure.placeInLattice(qeLattice)

        # collect atoms that are at equivalent position to some previous atom
        duplicates = set([
            a1 for i0, a0 in enumerate(reducedStructure)
            for a1 in reducedStructure[i0 + 1:] if a0.element == a1.element
            and equalPositions(a0.xyz, a1.xyz, eps=1e-4)
        ])

        # Filter out duplicate atoms.  Use slice assignment so that
        # reducedStructure is not replaced with a list.
        self.lattice = qeLattice
        self[:] = [a for a in reducedStructure if not a in duplicates]

        self._qeInput.structure = self
        return
Ejemplo n.º 10
0
    def _setStructureFromDiffpyStructure(self,
                                         structure,
                                         massList=[],
                                         psList=[],
                                         ibrav=0):
        """
        structure - diffpy.Structure object
        ibrav - Lattice index
        psList - list of strings with potential names
        diffpyStructure object will be modified with reduced atomic positions
        """
        diffpyLattice = structure.lattice

        qeLattice = QELattice(ibrav=0, base=diffpyLattice.base)
        qeLattice.a = 1.889725989 * qeLattice.a
        qeLattice._qeInput = self._qeInput

        self.lattice = qeLattice
        self.lattice.type = 'generic cubic'

        atomNames = []
        for a in structure:
            if self._element(a) not in atomNames:
                atomNames.append(self._element(a))

        atomicSpecies = {}
        for i, elem in enumerate(atomNames):
            if len(massList) - 1 < i:
                mass = 0
            else:
                mass = massList[i]
            if len(psList) - 1 < i:
                ps = ''
            else:
                ps = psList[i]
            atomicSpecies[elem] = (mass, ps)

        self[:] = []
        for atom in structure:
            elem = self._element(atom)
            self.addNewAtom(atype = elem, xyz = atom.xyz, \
                            mass = atomicSpecies[elem][0], \
                            potential = atomicSpecies[elem][1],\
                            lattice = self.lattice, optConstraint = [])
Ejemplo n.º 11
0
 def setStructureFromPWSCF(self):
     """ Loads structure from PWSCF config file"""
     self.lattice = QELattice(fname=self.filename)
     self.structure = Structure(lattice=self.lattice.diffpy())
     self.nat = int(self.qeConf.namelist('system').param('nat'))
     self.ntyp = int(self.qeConf.namelist('system').param('ntyp'))
     atomicLines = self.qeConf.card('atomic_positions').getLines()
     self.atomicPositionsType = self.qeConf.card(
         'atomic_positions').argument()
     if self.atomicPositionsType == 'bohr' or self.atomicPositionsType == 'angstrom':
         raise NotImplementedError
     if self.atomicPositionsType == None:
         self.atomicPositionsType = 'alat'
     for line in atomicLines:
         if '!' not in line:
             words = line.split()
             coords = [float(w) for w in words[1:4]]
             constraint = []
             if len(words) > 4:
                 constraint = [int(c) for c in words[4:7]]
             self.optConstraints.append(numpy.array(constraint, dtype=int))
             atomSymbol = words[0]
             if self.atomicPositionsType == 'alat':
                 coords = self.lattice.diffpy().fractional(
                     numpy.array(coords[0:3]) * self.lattice.a0)
             if self.atomicPositionsType == 'crystal':
                 coords = numpy.array(coords[0:3])
             self.structure.addNewAtom(atomSymbol,
                                       xyz=numpy.array(coords[0:3]))
             # parse mass ATOMIC_SPECIES section:
             atomicSpeciesLines = self.qeConf.card(
                 'atomic_species').getLines()
             for line in atomicSpeciesLines:
                 if '!' not in line:
                     atomicSpeciesWords = line.split()
                     element = atomicSpeciesWords[0]
                     mass = float(atomicSpeciesWords[1])
                     ps = atomicSpeciesWords[2]
                     self.atomicSpecies[element] = AtomicSpecies(
                         element, mass, ps)
Ejemplo n.º 12
0
    def __init__(self, atype=None, xyz=None, mass = None,  \
                 potential = None, lattice=None, optConstraint = None, name=None):
        """Create atom of a specified type at given lattice coordinates.
        Atom(a) creates a copy of Atom instance a.

        atype         -- element symbol string or Atom instance
        xyz           -- fractional(crystal) coordinates
        name          -- atom label
        mass          -- atom mass
        potential     -- pseudopotential file name
        lattice       -- QE coordinate system for fractional coordinates
        optConstraint -- list of up to three constraints for each coordinate 
                         for QE geometry optimization (0 or 1)
        """
        # declare data members
        self.element = None
        self._xyz = numpy.zeros(3, dtype=float)
        self.name = ''
        self._mass = 0
        self._potential = ''
        self.lattice = QELattice()
        from pwinput import PWInput
        self.lattice._qeInput = PWInput()
        self._optConstraint = numpy.array([], dtype = int)
        # assign them as needed
        if isinstance(atype, QEAtom):
            atype_dup = atype.__copy__()
            self.__dict__.update(atype_dup.__dict__)
        else:
            self.element = atype
        # take care of remaining arguments
        if xyz is not None:             self._xyz[:] = xyz
        if name is not None:            self.name = name
        if mass is not None:            self._mass = mass
        if potential is not None:       self._potential = potential
        if lattice is not None:         self.lattice = lattice
        if optConstraint is not None:  self._optConstraint = \
                                        numpy.array(optConstraint, dtype = int)
        return
Ejemplo n.º 13
0
 def __init__(self, qeConf):
     """the structure is initialized from PWSCF config file
        'lattice' and 'structure' are automatically updated"""
     self.filename = qeConf.filename
     self.atomicSpecies = OrderedDict()
     self.formatString = "%# .8f %# .8f %# .8f"
     # optConstraints three 1/0 for each coordinate of each atom
     self.optConstraints = []
     self.qeConf = qeConf
     self.lattice = QELattice()
     self.structure = Structure(lattice=self.lattice.diffpy())
     self.nat = None
     self.ntyp = None
Ejemplo n.º 14
0
    def __init__(self, atoms=[], lattice=None, filename=None, qeInput=None):
        """
        atoms        -- list of QEAtom atom instances or a QEStructure object
        lattice      -- QELattice object
        filename     -- filename QE input file 
        qeInput      -- pointer to a PWInput parsing object. If not None, 
                        its PWInput.structure and PWInput.structure.lattice 
                        will be  reset to the current instance of the structure       
        """

        Structure.__init__(self)
        self.formatString = '%# .8f %# .8f %# .8f'
        self._atomicPositionsType = 'crystal'
        self._qeInput = qeInput
        self.lattice = QELattice()
        self.lattice._qeInput = self._qeInput

        if lattice != None:

            if self.lattice._qeInput != None:
                self._qeInput = self.lattice._qeInput
            else:
                self.lattice._qeInput = self._qeInput

            self.lattice = QELattice(lattice=lattice)

        # CP and PW inputs are compatible
        from pwinput import PWInput
        from cpinput import CPInput
        if isinstance(atoms, PWInput) or isinstance(atoms, CPInput):
            qeInput = atoms
        elif isinstance(atoms, QEStructure):
            stru = atoms
            self.__dict__.update(stru.__dict__)
            # deep copy of the lattice will deep copy PWInput as well
            self.lattice = QELattice(lattice=stru.lattice)
            self._qeInput = self.lattice._qeInput
            self[:] = stru
        else:
            if self.lattice == None:
                raise "Lattice must be provided"
            self[:] = atoms

        if filename != None:
            qeInput = PWInput(filename=filename)
            qeInput.parse()
            self.parseInput(qeInput)

        if qeInput != None:
            self.lattice._qeInput = qeInput
            self._qeInput = qeInput

        if self.lattice._qeInput != None:
            self.lattice._qeInput.structure = self
            self.lattice._qeInput.structure.lattice = self.lattice

        return
Ejemplo n.º 15
0
 def setStructureFromQEInput(self):
     """ Loads structure from PWSCF config file"""
     self.atomicSpecies = OrderedDict()
     self.lattice = QELattice(qeConf = self.qeConf)
     self.structure = Structure(lattice = self.lattice.matter())
     self.nat  = int(self.qeConf.namelist('system').param('nat'))
     self.ntyp  = int(self.qeConf.namelist('system').param('ntyp'))
     atomicLines = self.qeConf.card('atomic_positions').lines()
     self.atomicPositionsType = self.qeConf.card('atomic_positions').arg()
     if self.atomicPositionsType == 'bohr' or self.atomicPositionsType == 'angstrom':
         raise NotImplementedError
     if self.atomicPositionsType == None:
         self.atomicPositionsType = 'alat'
     for line in atomicLines:
         if '!' not in line:
             words = line.split()
             coords = [float(w) for w in words[1:4]]
             constraint = []
             if len(words) > 4:
                 constraint = [int(c) for c in words[4:7]]
             self.optConstraints.append(numpy.array(constraint, dtype = int))
             atomSymbol = words[0]
             if self.atomicPositionsType == 'alat':
                 coords = self.lattice.matter().fractional(numpy.array(coords[0:3])*self.lattice.a0)
             if self.atomicPositionsType == 'crystal':
                 coords = numpy.array(coords[0:3])
             self.structure.addNewAtom(atomSymbol, xyz = numpy.array(coords[0:3]))
             # parse mass ATOMIC_SPECIES section:
             atomicSpeciesLines = self.qeConf.card('atomic_species').lines()
             for line in atomicSpeciesLines:
                 if '!' not in line:
                     atomicSpeciesWords = line.split()
                     element = atomicSpeciesWords[0]
                     mass = float(atomicSpeciesWords[1])
                     ps = atomicSpeciesWords[2]
                     self.atomicSpecies[element] =  AtomicSpecies(element, mass, ps)
Ejemplo n.º 16
0
    def setStructureFromPWOutput(self, pwscfOutputFile):
        """
        Loads structure from PWSCF output file. If there was geometry
        optimization (relax or vc-relax), the structure will be reinitialized
        from the last step of the optimization
        """
        file = open(pwscfOutputFile)
        pwscfOut = file.readlines()
        pseudoList = []
        atomList = []
        massList = []
        self.atomicSpecies = OrderedDict()
        self.atomicPositionsType = 'alat'
        # parse beginning:
        for i, line in enumerate(pwscfOut):
            if 'lattice parameter (a_0)' in line:
                a_0 = float(line.split()[4])
            if 'bravais-lattice index' in line:
                ibrav = int(line.split('=')[1])
            if 'number of atoms/cell' in line:
                self.nat = int(line.split('=')[1])
            if 'number of atomic types' in line:
                self.ntyp = int(line.split('=')[1])
            if 'PseudoPot.' in line:
                pseudoList.append(line.split('read from file')[1].strip())
            if 'atomic species   valence    mass     pseudopotential' in line:
                for j in range(self.ntyp):
                    atomList.append(pwscfOut[i + j + 1].split()[0])
                    massList.append(float(pwscfOut[i + j + 1].split()[2]))
            if 'crystal axes: (cart. coord. in units of a_0)' in line:
                latticeVectors = [
                    [float(f) * a_0 for f in pwscfOut[i + 1].split()[3:6]],
                    [float(f) * a_0 for f in pwscfOut[i + 2].split()[3:6]],
                    [float(f) * a_0 for f in pwscfOut[i + 3].split()[3:6]]
                ]
                self.lattice = QELattice(ibrav=ibrav, base=latticeVectors)
            if 'site n.     atom                  positions (a_0 units)' in line:
                self.structure = Structure(lattice=self.lattice.matter())
                for n in range(self.nat):
                    words = pwscfOut[i + n + 1].split()
                    atomSymbol = words[1]
                    coords = [float(w) for w in words[6:9]]
                    constraint = []
                    self.optConstraints.append(
                        numpy.array(constraint, dtype=int))
                    #print numpy.array(coords[0:3])*a_0
                    coords = self.lattice.matter().fractional(
                        numpy.array(coords[0:3]) * a_0)
                    self.structure.addNewAtom(atomSymbol,
                                              xyz=numpy.array(coords[0:3]))

        for a, m, p in zip(atomList, massList, pseudoList):
            self.atomicSpecies[a] = AtomicSpecies(a, m, p)

        #print 'Input structure from output file: ', self.toString()
        #Parse end:
        # Find all geometry optimization steps
        posList = [
            i for i, line in enumerate(pwscfOut) if '!    total energy' in line
        ]
        lastSection = pwscfOut[posList[-1]:]
        for i, line in enumerate(lastSection):
            if 'CELL_PARAMETERS (alat)' in line:
                latticeVectors = [
                    [float(f) * a_0 for f in lastSection[i + 1].split()],
                    [float(f) * a_0 for f in lastSection[i + 2].split()],
                    [float(f) * a_0 for f in lastSection[i + 3].split()]
                ]
                self.lattice = QELattice(ibrav=ibrav, base=latticeVectors)
                print self.lattice.matter().base
            if 'ATOMIC_POSITIONS (alat)' in line:
                self.structure = Structure(lattice=self.lattice.matter())
                for n in range(self.nat):
                    words = lastSection[i + n + 1].split()
                    atomSymbol = words[0]
                    coords = [float(w) for w in words[1:4]]
                    constraint = []
                    if len(words) > 4:
                        constraint = [int(c) for c in words[4:7]]
                    self.optConstraints.append(
                        numpy.array(constraint, dtype=int))
                    #print numpy.array(coords[0:3])*a_0
                    coords = self.lattice.matter().fractional(
                        numpy.array(coords[0:3]) * a_0)
                    self.structure.addNewAtom(atomSymbol,
                                              xyz=numpy.array(coords[0:3]))
Ejemplo n.º 17
0
    def _setReducedStructureFromDiffpyStructure(self,
                                                structure,
                                                ibrav,
                                                massList=[],
                                                psList=[]):
        """
        structure - diffpy.Structure object
        ibrav - Lattice index
        psList - list of strings with potential names
        diffpyStructure object will be modified with reduced atomic positions
        """
        import copy

        diffpyLattice = copy.deepcopy(structure.lattice)

        a = diffpyLattice.a
        b = diffpyLattice.b
        c = diffpyLattice.c
        cAB = cosd(diffpyLattice.gamma)
        cBC = cosd(diffpyLattice.alpha)
        cAC = cosd(diffpyLattice.beta)

        qeLattice = QELattice(ibrav = ibrav, a = a, b = b, c = c,  cBC =  cBC, \
                              cAC = cAC, cAB = cAB)

        qeLattice._qeInput = self._qeInput
        self.lattice = qeLattice
        # make a deep copy:
        reducedStructure = Structure(atoms=structure)

        reducedStructure.placeInLattice(Lattice(base=qeLattice.diffpy().base))

        # collect atoms that are at equivalent position to some previous atom
        duplicates = set([
            a1 for i0, a0 in enumerate(reducedStructure)
            for a1 in reducedStructure[i0 + 1:]
            if self._element(a0) == self._element(a1)
            and equalPositions(a0.xyz, a1.xyz, eps=1e-4)
        ])

        # Filter out duplicate atoms.  Use slice assignment so that
        # reducedStructure is not replaced with a list.
        reducedStructure[:] = [
            a for a in reducedStructure if not a in duplicates
        ]

        atomNames = []
        for a in reducedStructure:
            if self._element(a) not in atomNames:
                atomNames.append(self._element(a))

        atomicSpecies = {}
        for i, elem in enumerate(atomNames):
            if len(massList) - 1 < i:
                mass = 0
            else:
                mass = massList[i]
            if len(psList) - 1 < i:
                ps = ''
            else:
                ps = psList[i]
            atomicSpecies[elem] = (mass, ps)

        self[:] = []

        # convert to bohr units
        self.lattice.setLattice(ibrav, self.lattice.a*1.889725989, \
                                 self.lattice.b*1.889725989,
                                 self.lattice.c*1.889725989)

        for atom in reducedStructure:
            elem = self._element(atom)
            self.addNewAtom(atype = elem, xyz = atom.xyz, \
                            mass = atomicSpecies[elem][0], \
                            potential = atomicSpecies[elem][1],\
                            lattice = self.lattice, optConstraint = [])
Ejemplo n.º 18
0
 def _setReducedStructureFromMatterStructure(self,
                                             structure,
                                             ibrav,
                                             massList=[],
                                             psList=[]):
     """
     structure - matter.Structure object
     ibrav - Lattice index
     psList - list of strings with potential names
     matterStructure object will be modified with reduced atomic positions
     """
     import copy
     matterLattice = copy.deepcopy(structure.lattice)
     # there is a big problem with what Nikolay is doing because he is initializing QELattice with abcalbega which
     # leaves room for errors in how the basis vectors are chosen compared to the old lattice
     a = matterLattice.a
     b = matterLattice.b
     c = matterLattice.c
     cAB = cosd(matterLattice.gamma)
     cBC = cosd(matterLattice.alpha)
     cAC = cosd(matterLattice.beta)
     qeLattice = QELattice(ibrav = ibrav, a = a, b = b, c = c,  cBC =  cBC, \
                           cAC = cAC, cAB = cAB)
     #what he should do is initialize the vectors of QELattice with the vectors of matterLattice
     #
     qeLattice._qeInput = self._qeInput
     self.lattice = qeLattice
     # make a deep copy:
     reducedStructure = Structure(atoms=structure)
     reducedStructure.placeInLattice(Lattice(base=qeLattice.matter().base))
     # collect atoms that are at equivalent position to some previous atom
     duplicates = set([
         a1 for i0, a0 in enumerate(reducedStructure)
         for a1 in reducedStructure[i0 + 1:] if a0.symbol == a1.symbol
         and equalPositions(a0.xyz, a1.xyz, eps=1e-4)
     ])
     # Filter out duplicate atoms.  Use slice assignment so that
     # reducedStructure is not replaced with a list.
     reducedStructure[:] = [
         a for a in reducedStructure if not a in duplicates
     ]
     atomNames = []
     for a in reducedStructure:
         if a.symbol not in atomNames:
             atomNames.append(a.symbol)
     atomicSpecies = {}
     for i, elem in enumerate(atomNames):
         if len(massList) - 1 < i:
             mass = 0
         else:
             mass = massList[i]
         if len(psList) - 1 < i:
             ps = ''
         else:
             ps = psList[i]
         atomicSpecies[elem] = (mass, ps)
     self[:] = []
     # convert to bohr units
     self.lattice.setLattice(ibrav, self.lattice.a*1.889725989, \
                              self.lattice.b*1.889725989,
                              self.lattice.c*1.889725989)
     for atom in reducedStructure:
         self.addNewAtom(atype = atom.symbol, xyz = atom.xyz, \
                         mass = atomicSpecies[atom.symbol][0], \
                         potential = atomicSpecies[atom.symbol][1],\
                         lattice = self.lattice, optConstraint = [])
Ejemplo n.º 19
0
    def setReducedStructureFromDiffpyStructure(self, structure, ibrav, massList = [], psList = []):
        """
        structure - diffpy.Structure object
        ibrav - Lattice index
        psList - list of strings with pseudopotential names
        diffpyStructure object will be modified with reduced atomic positions
        """

        #self.atomicSpecies = OrderedDict()
        #self.optConstraints = []
        #self.atomicPositionsType = 'crystal'

        diffpyLattice = structure.lattice

        a = diffpyLattice.a
        b = diffpyLattice.b
        c = diffpyLattice.c
        cAB = cosd(diffpyLattice.gamma)
        cBC = cosd(diffpyLattice.alpha)
        cAC = cosd(diffpyLattice.beta)

        qeLattice = QELattice(ibrav = ibrav, a = a, b = b, c = c,  cBC =  cBC, \
                              cAC = cAC, cAB = cAB)

        self.lattice = qeLattice
        # make a deep copy (does not wok now)
        #reducedStructure = Structure(diffpyStructure)
        reducedStructure = structure

        reducedStructure.placeInLattice(Lattice(base=qeLattice.diffpy().base))

        # collect atoms that are at equivalent position to some previous atom
        duplicates = set([a1
            for i0, a0 in enumerate(reducedStructure) for a1 in reducedStructure[i0+1:]
                if   self._element(a0) == self._element(a1) and equalPositions(a0.xyz, a1.xyz, eps=1e-4)])

        
        # Filter out duplicate atoms.  Use slice assignment so that
        # reducedStructure is not replaced with a list.
        reducedStructure[:] = [a for a in reducedStructure if not a in duplicates]

        self.structure = reducedStructure

        atomNames = []
        for a in reducedStructure:
            if self._element(a) not in atomNames:
                atomNames.append(self._element(a))
        
        #print atomNames
        #print len(massList)
        for i, elem in enumerate(atomNames):
            if len(massList) - 1 < i:
                mass = 0
            else:
                mass = massList[i]
            if len(psList) - 1 < i:
                ps = ''
            else:
                ps = psList[i]      
            #print mass, ps
            # atomDict[a] = 
        # for i, atom in enumerate(reducedStructure):
            # elem = self._element(atom)
            # if len(massList) - 1 < i:
                # mass = 0
            # else:
                # mass = massList[i]
            # if len(psList) - 1 < i:
                # ps = ''
            # else:
                # ps = psList[i]            
            self.atomicSpecies[elem] =  AtomicSpecies(elem, mass, ps)
        
        for atom in reducedStructure:
            self.optConstraints.append([])

        # convert to bohr units
        self.lattice.setLattice(ibrav, self.lattice.a*1.889725989, \
                                 self.lattice.b*1.889725989,
                                 self.lattice.c*1.889725989)

        self.nat = len(reducedStructure)
        self.ntyp = len(self.atomicSpecies)
Ejemplo n.º 20
0
class QEStructure(Structure):
    """ 
    Parses and handles Quantum Espresso structure information. 
    QEStructure is inherited from a diffpy.Structure, which is in turn 
    inherited from a Python list. 
    QEStructure is a list of QEAtom object instances.
    All list functionality is preserved.  setitem and 
    setslice methods are overloaded so that the lattice attribute 
    of atoms get set to lattice.
       
    QEStructure, QELattice and QEAtom objects  contain a hidden QEInput 
    pointer to current Quantum Espresso parsing object. 
    if QEInput.autoUpdate = True (default),any change in a property from
    QEStructure, QELattice, or QEAtom will automatically invoke 
    QEInput.update(). In that case, QEInput.save() or QEInput.toString() will
    immediately yield updated QE input file or a string
       
    All properties are mutually synchronized. E.g. change in a lattice parameter
    will also affect other lattice parameters as well as atomic positions  
    according to the lattice type (ibrav)
    
    
    All relevant methods from diffpy.Structure are redefined.
    
    
    QEStructure read only properties, automatically synchronized with current 
    list of Atoms:
      
    nat                  -- "number of atoms" (integer)
    
    ntyp                 -- "number of atomic types" (integer)
    
    atomicSpecies        -- OrderedDic of AtomicSpecies class instances 
                            (see AtomicSpecies class definition)
                           
    Other properties:
    
    atomicPositionsType  -- 'crystal' (default), 'alat', 'bohr', and 'angstrom'
         
                           
    Note: This version of QEStructure does not support multiple images from 
    QE input files
    
    """
    def __init__(self, atoms=[], lattice=None, filename=None, qeInput=None):
        """
        atoms        -- list of QEAtom atom instances or a QEStructure object
        lattice      -- QELattice object
        filename     -- filename QE input file 
        qeInput      -- pointer to a PWInput parsing object. If not None, 
                        its PWInput.structure and PWInput.structure.lattice 
                        will be  reset to the current instance of the structure       
        """

        Structure.__init__(self)
        self.formatString = '%# .8f %# .8f %# .8f'
        self._atomicPositionsType = 'crystal'
        self._qeInput = qeInput
        self.lattice = QELattice()
        self.lattice._qeInput = self._qeInput

        if lattice != None:

            if self.lattice._qeInput != None:
                self._qeInput = self.lattice._qeInput
            else:
                self.lattice._qeInput = self._qeInput

            self.lattice = QELattice(lattice=lattice)

        # CP and PW inputs are compatible
        from pwinput import PWInput
        from cpinput import CPInput
        if isinstance(atoms, PWInput) or isinstance(atoms, CPInput):
            qeInput = atoms
        elif isinstance(atoms, QEStructure):
            stru = atoms
            self.__dict__.update(stru.__dict__)
            # deep copy of the lattice will deep copy PWInput as well
            self.lattice = QELattice(lattice=stru.lattice)
            self._qeInput = self.lattice._qeInput
            self[:] = stru
        else:
            if self.lattice == None:
                raise "Lattice must be provided"
            self[:] = atoms

        if filename != None:
            qeInput = PWInput(filename=filename)
            qeInput.parse()
            self.parseInput(qeInput)

        if qeInput != None:
            self.lattice._qeInput = qeInput
            self._qeInput = qeInput

        if self.lattice._qeInput != None:
            self.lattice._qeInput.structure = self
            self.lattice._qeInput.structure.lattice = self.lattice

        return

    def addNewAtom(self, *args, **kwargs):
        """Add new Atom instance to the end of this Structure.

        All arguments are forwarded to Atom constructor.

        No return value.
        """
        kwargs['lattice'] = self.lattice
        a = QEAtom(*args, **kwargs)
        list.append(self, a)
        self._uncache('labels')
        return

    def placeInLattice(self, new_lattice):
        """place structure into new_lattice coordinate system

        sets lattice to new_lattice and recalculate fractional coordinates
        of all atoms so their absolute positionls remain the same

        return self
        """
        Tx = numpy.dot(self.lattice.diffpy().base,
                       new_lattice.diffpy().recbase)
        Tu = numpy.dot(self.lattice.diffpy().normbase,
                       new_lattice.diffpy().recnormbase)
        for a in self:
            a.xyz = numpy.dot(a.xyz, Tx)
        tmpInput = self.lattice._qeInput
        self.lattice = new_lattice
        self.lattice._qeInput = tmpInput
        self.lattice._qeInput.structure = self
        return self

    ##############################################################################
    # overloaded list methods - taken from diffpy.Structure
    ##############################################################################

    def append(self, a, copy=True):
        """Append atom to a structure and update its lattice attribute.

        a    -- instance of QEAtom
        copy -- flag for appending a copy of a.
                When False, append a and update a.owner.

        No return value.
        """
        self._uncache('labels')
        adup = copy and QEAtom(a) or a
        adup.lattice = self.lattice
        list.append(self, adup)
        if hasattr(self, '_qeInput') and self._qeInput != None:
            self._qeInput.update()
        return

    def insert(self, idx, a, copy=True):
        """Insert atom a before position idx in this Structure.

        idx  -- position in atom list
        a    -- instance of QEAtom
        copy -- flag for inserting a copy of a.
                When False, append a and update a.lattice.

        No return value.
        """
        self._uncache('labels')
        adup = copy and QEAtom(a) or a
        adup.lattice = self.lattice
        list.insert(self, idx, adup)
        if hasattr(self, '_qeInput') and self._qeInput != None:
            self._qeInput.update()
        return

    def extend(self, atoms, copy=True):
        """Extend Structure by appending copies from a list of atoms.

        atoms -- list of QEAtom instances
        copy  -- flag for extending with copies of QEAtom instances.
                 When False extend with atoms and update their lattice
                 attributes.

        No return value.
        """
        self._uncache('labels')
        if copy: adups = [QEAtom(a) for a in atoms]
        else: adups = atoms
        for a in adups:
            a.lattice = self.lattice
        list.extend(self, adups)
        if hasattr(self, '_qeInput') and self._qeInput != None:
            self._qeInput.update()
        return

    def __setitem__(self, idx, a, copy=True):
        """Set idx-th atom to a.

        idx  -- index of atom in this Structure
        a    -- instance of QEAtom
        copy -- flag for setting to a copy of a.
                When False, set to a and update a.lattice.

        No return value.
        """
        self._uncache('labels')
        adup = copy and QEAtom(a) or a
        adup.lattice = self.lattice
        list.__setitem__(self, idx, adup)
        if hasattr(self, '_qeInput') and self._qeInput != None:
            self._qeInput.update()
        return

    def __setslice__(self, lo, hi, atoms, copy=True):
        """Set Structure slice from lo to hi-1 to the sequence of atoms.

        lo    -- low index for the slice
        hi    -- high index of the slice
        atoms -- sequence of Atom instances
        copy  -- flag for using copies of Atom instances.  When False, set
                 to existing instances and update their lattice attributes.

        No return value.
        """
        self._uncache('labels')
        if copy: adups = [QEAtom(a) for a in atoms]
        else: adups = atoms
        for a in adups:
            a.lattice = self.lattice
        list.__setslice__(self, lo, hi, adups)
        if hasattr(self, '_qeInput') and self._qeInput != None:
            self._qeInput.update()
        return

    def _get_nat(self):
        return len(self)

    nat = property(_get_nat, doc="number of atoms")

    def _get_ntyp(self):
        return len(self.atomicSpecies)

    ntyp = property(_get_ntyp, doc="number of types")

    def _get_atomicPositionsType(self):
        return self._atomicPositionsType

    def _set_atomicPositionsType(self, value):
        self._atomicPositionsType = value
        self.lattice._qeInput.update()

    atomicPositionsType = property(_get_atomicPositionsType, \
                                   _set_atomicPositionsType, \
                           doc ="type of atomic positions (crystal, alat ...)")

    def _get_atomicSpecies(self):
        atomicSpecies = OrderedDict()
        for a in self:
            atomicSpecies[a.element] = AtomicSpecies(element = a.element, \
                                        mass = a.mass, potential = a.potential)

        return atomicSpecies

    atomicSpecies = property(_get_atomicSpecies, \
              doc ="returns an ordered dictionary with atomic species' objects")

    def __str__(self):
        """simple string representation"""
        s = str(self.lattice) + '\n'
        if self.atomicPositionsType == 'alat':
            s = s + 'Atomic positions in units of lattice parametr "a":\n'
        if self.atomicPositionsType == 'crystal':
            s = s + 'Atomic positions in crystal coordinates:\n'
        for atom in self:
            constraint = atom.optConstraint
            if self.atomicPositionsType == 'alat':
                coords = self.lattice.diffpy().cartesian(
                    atom.xyz) / self.lattice.a
                coords = self.formatString % (coords[0], coords[1], coords[2])
            else:
                if self.atomicPositionsType == 'crystal':
                    coords = self.formatString % (atom.xyz[0], atom.xyz[1],
                                                  atom.xyz[2])
                else:
                    raise NonImplementedError
            s = s + '%-3s'%self._element(atom) + '    ' + coords + '  ' \
                    + str(constraint)[1:-1] + '\n'
        s = s + '\n'

        for element, specie in self.atomicSpecies.items():
            s = s + specie.toString() + '\n'

        return s

    def atomLabels(self):
        labels = []
        for l in self.atomicSpecies:
            labels.append(l)
        return labels

    def parseInput(self, qeInput):
        from qecalc.qetask.qeparser.qestructureparser.qestructureparser import QEStructureParser
        new_structure = QEStructureParser(qeInput).parseqeInput()
        self.__Init(new_structure)

    def __Init(self, structure):
        QEStructure.__init__(self)
        if structure is not None:
            self.__dict__.update(structure.__dict__)
            self.lattice.__dict__.update(structure.lattice.__dict__)
            self[:] = structure

    def read(self, filename, format='pwinput'):
        """Load structure from a file, any original data become lost.
l
        filename -- file to be loaded
        format   -- structure formats
                    'pwinput' ( pw.x input, default), 'pwoutput' (pw.x output),
                    'bratoms', 'cif', 'discus', 'pdb', 'pdffit', 'rawxyz', 
                    'xcfg', 'xyz'

        Return instance of data Parser used to process file.  This
        can be inspected for information related to particular format.
        """
        from qecalc.qetask.qeparser.qestructureparser import parser_index
        if self._qeInput == None:
            from qecalc.qetask.qeparser.pwinput import PWInput
            self._qeInput = PWInput()
            self._qeInput.structure = self
            #self._qeInput.parse()

        if format in parser_index:
            module = __import__("qestructureparser.P_" + format, globals(), \
                                locals(), ['P_' + format], -1)
            parser = module.getParser(self._qeInput)
            new_structure = parser.parse(filename)
        else:
            diffpyStruct = Structure()
            parser = diffpyStruct.read(filename, format=format)
            new_structure = QEStructure(qeInput=self._qeInput)
            new_structure._setStructureFromDiffpyStructure(diffpyStruct, \
                                        massList = [], psList = [], ibrav = 0)

        new_structure.lattice._qeInput.update(forceUpdate=True)
        self.__Init(new_structure)
        return parser

    def readStr(self, s, format='pwinput'):
        """Load structure from a string, any original data become lost.

        filename -- file to be loaded
        format   -- structure formats
                    'pwinput' ( pw.x input, default), 'pwoutput' (pw.x output),
                    'bratoms', 'cif', 'discus', 'pdb', 'pdffit', 'rawxyz', 
                    'xcfg', 'xyz'

        Return instance of data Parser used to process file.  This
        can be inspected for information related to particular format.
        """
        from qecalc.qetask.qeparser.qestructureparser import parser_index
        #from qecalc.qetask.qeparser.pwinput import PWInput

        if self._qeInput == None:
            from qecalc.qetask.qeparser.pwinput import PWInput
            self._qeInput = PWInput()
            self._qeInput.structure = self
        #if self._qeInput == None:
        #    self._qeInput = PWInput()
        #self._qeInput.parse()

        if format in parser_index:
            module = __import__("qestructureparser.P_" + format, globals(), \
                                locals(), ['P_' + format], -1)
            parser = module.getParser(self._qeInput)
            new_structure = parser.parseStr(s)
        else:
            diffpyStruct = Structure()
            parser = diffpyStruct.readStr(s, format=format)
            new_structure = QEStructure(qeInput=self._qeInput)
            new_structure._setStructureFromDiffpyStructure(diffpyStruct, \
                                        massList = [], psList = [], ibrav = 0)

        new_structure.lattice._qeInput.update(forceUpdate=True)
        #new_structure._qeInput = new_structure.lattice._qeInput
        #print 'toString: ', new_structure._qeInput.toString()
        #self = QEStructure(new_structure)
        self.__Init(new_structure)
        return parser

    def write(self, filename=None, format="pwinput"):
        """Save structure to file in the specified format
        
        format   -- structure formats
                    'pwinput' ( pw.x input, default), 'pwoutput' (pw.x output),
                    'bratoms', 'cif', 'discus', 'pdb', 'pdffit', 'rawxyz', 
                    'xcfg', 'xyz'        
                    
        No return value.
        """
        if format == "pwinput":
            self._qeInput.update(forceUpdate=True)
            if filename == None:
                filename = self._qeInput.filename
            input = QEInput(config=self._qeInput.toString(), type='pw')
            input.save(filename=filename)
        else:
            self.diffpy().write(filename=filename, format=format)
        return

    def writeStr(self, format='pwinput'):
        """return string representation of the structure in specified format
        
        format   -- structure formats
                    'pwinput' ( pw.x input, default), 'pwoutput' (pw.x output),
                    'bratoms', 'cif', 'discus', 'pdb', 'pdffit', 'rawxyz', 
                    'xcfg', 'xyz'
        """

        if format == 'pwinput':
            self._qeInput.update(forceUpdate=True)
            return self.toString()
        else:
            return self.diffpy().writeStr(format=format)

    def toString(self, string=None):
        """Writes/updates structure into PW config string.
           If the string is None, one, based on current structure 
           will be generated
           """
        if string != None:
            stru = QEStructure()
            stru.readStr(string, format='pwinput')
            qeInput = stru._qeInput
        else:
            qeInput = self._qeInput

        self._qeInput.update(qeInput=qeInput, forceUpdate=True)
        return qeInput.toString()

    def save(self, filename=None):
        """Writes/updates structure into PW config file,
           if the file does not exist, new one will be created"""
        from os.path import exists
        from qecalc.qetask.qeparser.pwinput import PWInput
        if filename != None:
            if not exists(filename):
                f = open(filename, 'w')
            qeInput = PWInput()
            qeInput.readFile(filename)
        else:
            qeInput = self._qeInput
        self._qeInput.update(qeInput=qeInput, forceUpdate=True)
        qeInput.save()

    def reduce(self, ibrav=None):
        """
        Reduces a structure instance with nonprimitive lattice ( i.e. solves
        for equivalent atomic positions) according to specified  lattice type (ibrav). 
        Each ibrav number corresponds to a specific primitive lattice 
        (see QE documentation).         
        """

        ib = self.lattice.ibrav
        if ibrav != None:
            ib = ibrav

        a = self.lattice.diffpy().a
        b = self.lattice.diffpy().b
        c = self.lattice.diffpy().c
        cAB = cosd(self.lattice.diffpy().gamma)
        cBC = cosd(self.lattice.diffpy().alpha)
        cAC = cosd(self.lattice.diffpy().beta)
        if ib > 0:
            qeLattice = QELattice(ibrav = ib, a = a, b = b, c = c,  cBC =  cBC, \
                              cAC = cAC, cAB = cAB)
        else:
            qeLattice = QELattice(ibrav=ib, base=self.lattice.base)

        qeLattice._qeInput = self._qeInput

        reducedStructure = QEStructure(self)

        reducedStructure.placeInLattice(qeLattice)

        # collect atoms that are at equivalent position to some previous atom
        duplicates = set([
            a1 for i0, a0 in enumerate(reducedStructure)
            for a1 in reducedStructure[i0 + 1:] if a0.element == a1.element
            and equalPositions(a0.xyz, a1.xyz, eps=1e-4)
        ])

        # Filter out duplicate atoms.  Use slice assignment so that
        # reducedStructure is not replaced with a list.
        self.lattice = qeLattice
        self[:] = [a for a in reducedStructure if not a in duplicates]

        self._qeInput.structure = self
        return

    def load(self, source, **args):
        task = {
            'diffpy': self._setStructureFromDiffpyStructure,
            'matter': self._setStructureFromMatter,
        }

        if source == 'matter':
            if 'ibrav' in args and args['ibrav'] != 0:
                task['matter'] = self._setReducedStructureFromMatter

        if source == 'diffpy':
            if 'ibrav' in args and args['ibrav'] != 0:
                task['diffpy'] = self._setReducedStructureFromDiffpyStructure

        task[source](**args)

        self.lattice._qeInput.update()

    def _matter_diffpy(self, structure):
        """
        converts matter Structure object to diffpy.Structure
        returns diffpy.Structure object
        """
        l = structure.lattice
        lat = Lattice( a=l.a, b=l.b, c=l.c, alpha=l.alpha, \
                        beta=l.beta, gamma=l.gamma)
        stru = Structure(lattice=lat)
        for a in structure:
            stru.addNewAtom(atype = a.symbol, xyz = a.xyz, name = a.symbol, \
                       anisotropy=a.anisotropy, U=a.U, Uisoequiv=a.Uisoequiv, \
                       lattice=lat)
        return stru

    def _setStructureFromMatter(self,
                                structure,
                                massList=[],
                                psList=[],
                                ibrav=0):

        stru = self._matter_diffpy(structure)
        self._setStructureFromDiffpyStructure(structure=stru,\
                                  massList=massList, psList=psList,ibrav=ibrav)

    def _setReducedStructureFromMatter(self,
                                       structure,
                                       ibrav,
                                       massList=[],
                                       psList=[]):
        stru = self._matter_diffpy(structure)
        self._setReducedStructureFromDiffpyStructure(structure = stru, \
                                 ibrav=ibrav, massList=massList,psList=psList)

    def _setStructureFromDiffpyStructure(self,
                                         structure,
                                         massList=[],
                                         psList=[],
                                         ibrav=0):
        """
        structure - diffpy.Structure object
        ibrav - Lattice index
        psList - list of strings with potential names
        diffpyStructure object will be modified with reduced atomic positions
        """
        diffpyLattice = structure.lattice

        qeLattice = QELattice(ibrav=0, base=diffpyLattice.base)
        qeLattice.a = 1.889725989 * qeLattice.a
        qeLattice._qeInput = self._qeInput

        self.lattice = qeLattice
        self.lattice.type = 'generic cubic'

        atomNames = []
        for a in structure:
            if self._element(a) not in atomNames:
                atomNames.append(self._element(a))

        atomicSpecies = {}
        for i, elem in enumerate(atomNames):
            if len(massList) - 1 < i:
                mass = 0
            else:
                mass = massList[i]
            if len(psList) - 1 < i:
                ps = ''
            else:
                ps = psList[i]
            atomicSpecies[elem] = (mass, ps)

        self[:] = []
        for atom in structure:
            elem = self._element(atom)
            self.addNewAtom(atype = elem, xyz = atom.xyz, \
                            mass = atomicSpecies[elem][0], \
                            potential = atomicSpecies[elem][1],\
                            lattice = self.lattice, optConstraint = [])

    def _setReducedStructureFromDiffpyStructure(self,
                                                structure,
                                                ibrav,
                                                massList=[],
                                                psList=[]):
        """
        structure - diffpy.Structure object
        ibrav - Lattice index
        psList - list of strings with potential names
        diffpyStructure object will be modified with reduced atomic positions
        """
        import copy

        diffpyLattice = copy.deepcopy(structure.lattice)

        a = diffpyLattice.a
        b = diffpyLattice.b
        c = diffpyLattice.c
        cAB = cosd(diffpyLattice.gamma)
        cBC = cosd(diffpyLattice.alpha)
        cAC = cosd(diffpyLattice.beta)

        qeLattice = QELattice(ibrav = ibrav, a = a, b = b, c = c,  cBC =  cBC, \
                              cAC = cAC, cAB = cAB)

        qeLattice._qeInput = self._qeInput
        self.lattice = qeLattice
        # make a deep copy:
        reducedStructure = Structure(atoms=structure)

        reducedStructure.placeInLattice(Lattice(base=qeLattice.diffpy().base))

        # collect atoms that are at equivalent position to some previous atom
        duplicates = set([
            a1 for i0, a0 in enumerate(reducedStructure)
            for a1 in reducedStructure[i0 + 1:]
            if self._element(a0) == self._element(a1)
            and equalPositions(a0.xyz, a1.xyz, eps=1e-4)
        ])

        # Filter out duplicate atoms.  Use slice assignment so that
        # reducedStructure is not replaced with a list.
        reducedStructure[:] = [
            a for a in reducedStructure if not a in duplicates
        ]

        atomNames = []
        for a in reducedStructure:
            if self._element(a) not in atomNames:
                atomNames.append(self._element(a))

        atomicSpecies = {}
        for i, elem in enumerate(atomNames):
            if len(massList) - 1 < i:
                mass = 0
            else:
                mass = massList[i]
            if len(psList) - 1 < i:
                ps = ''
            else:
                ps = psList[i]
            atomicSpecies[elem] = (mass, ps)

        self[:] = []

        # convert to bohr units
        self.lattice.setLattice(ibrav, self.lattice.a*1.889725989, \
                                 self.lattice.b*1.889725989,
                                 self.lattice.c*1.889725989)

        for atom in reducedStructure:
            elem = self._element(atom)
            self.addNewAtom(atype = elem, xyz = atom.xyz, \
                            mass = atomicSpecies[elem][0], \
                            potential = atomicSpecies[elem][1],\
                            lattice = self.lattice, optConstraint = [])

    def updatePWInput(self, qeInput=None):
        """
        Deprecated
        """
        self._qeInput.update()

    def diffpy(self):
        stru = Structure(lattice=self.lattice.diffpy())
        for atom in self:
            stru.addNewAtom(atype = atom.element, xyz = atom.xyz, \
                                              lattice = self.lattice.diffpy() )
        return stru

    def _element(self, atom):
        """
        Is needed for support both diffpy and matter classes
        """
        if 'element' in dir(atom):
            return atom.element
        else:
            if 'symbol' in dir(atom):
                return atom.symbol
            else:
                raise
Ejemplo n.º 21
0
class QEStructure():
    
    def __init__(self, qeConf):
        """the structure is initialized from PWSCF config file
           'lattice' and 'structure' are automatically updated"""
        self.filename = qeConf.filename
        self.atomicSpecies = OrderedDict()
        self.formatString = '%# .8f %# .8f %# .8f'
        # optConstraints three 1/0 for each coordinate of each atom
        self.optConstraints = []
        self.qeConf = qeConf
        self.lattice = QELattice()
        self.structure = Structure(lattice = self.lattice.diffpy())
        self.nat = None
        self.ntyp = None
        self.atomicPositionsType = 'crystal'       
        

    def atomLabels(self):
        labels = []
        for l in self.atomicSpecies:
            labels.append(l)
        return labels
        
    def parseInput(self):
        self.setStructureFromQEInput()
        
    def parseOutput(self, pwscfOutputFile):
        self.setStructureFromPWOutput(pwscfOutputFile)

    def setStructureFromPWOutput(self, pwscfOutputFile): 
        """
        Loads structure from PWSCF output file. If there was geometry
        optimization (relax or vc-relax), the structure will be reinitialized
        from the last step of the optimization
        """
        file = open(pwscfOutputFile)
        pwscfOut = file.readlines()
        pseudoList = []
        atomList = []
        massList = []
        self.atomicSpecies = OrderedDict()
        self.atomicPositionsType = 'alat'  
        # parse beginning:
        for i, line in enumerate(pwscfOut):
            if 'lattice parameter (a_0)' in line:
                a_0 = float(line.split()[4])
            if 'bravais-lattice index' in line:
                ibrav = int(line.split('=')[1])
            if 'number of atoms/cell' in line:
                self.nat = int(line.split('=')[1])
            if 'number of atomic types' in line:
                self.ntyp = int(line.split('=')[1])
            if 'PseudoPot.' in line:
                pseudoList.append(line.split('read from file')[1].strip())
            if 'atomic species   valence    mass     pseudopotential' in line:
                for j in range(self.ntyp):
                    atomList.append(pwscfOut[i+j+1].split()[0])
                    massList.append(float(pwscfOut[i+j+1].split()[2]))
            if 'crystal axes: (cart. coord. in units of a_0)' in line:
                latticeVectors = [[float(f)*a_0 for f in pwscfOut[i + 1].split()[3:6] ],
                                  [float(f)*a_0 for f in pwscfOut[i + 2].split()[3:6] ],
                                  [float(f)*a_0 for f in pwscfOut[i + 3].split()[3:6] ]]
                self.lattice.setLatticeFromQEVectors(ibrav, latticeVectors)
            if 'site n.     atom                  positions (a_0 units)' in line:
                self.structure = Structure(lattice = self.lattice.diffpy())
                for n in range(self.nat):
                    words = pwscfOut[i + n + 1].split()
                    atomSymbol = words[1]
                    coords = [float(w) for w in words[6:9]]
                    constraint = []
                    self.optConstraints.append(numpy.array(constraint, dtype = int))
                    #print numpy.array(coords[0:3])*a_0
                    coords = self.lattice.diffpy().fractional(numpy.array(coords[0:3])*a_0)
                    self.structure.addNewAtom(atomSymbol, xyz = numpy.array(coords[0:3]))

        for a, m, p in zip(atomList, massList, pseudoList):
            self.atomicSpecies[a] = AtomicSpecies(a, m, p)

        #print 'Input structure from output file: ', self.toString()
        #Parse end:
        # Find all geometry optimization steps
        posList =  [i for i,line in enumerate(pwscfOut) if '!    total energy' in line]
        lastSection = pwscfOut[posList[-1]:]
        for i, line in enumerate(lastSection):
            if 'CELL_PARAMETERS (alat)' in line:
                latticeVectors = [[float(f)*a_0 for f in lastSection[i + 1].split() ],
                                  [float(f)*a_0 for f in lastSection[i + 2].split() ],
                                  [float(f)*a_0 for f in lastSection[i + 3].split() ]]
                self.lattice.setLatticeFromQEVectors(ibrav, latticeVectors)
                #self.lattice = QELattice(ibrav = ibrav, base = latticeVectors)
                #print self.lattice.diffpy().base
            if 'ATOMIC_POSITIONS (alat)' in line:
                self.structure = Structure(lattice = self.lattice.diffpy())
                for n in range(self.nat):
                    words = lastSection[i + n + 1].split()
                    atomSymbol = words[0]
                    coords = [float(w) for w in words[1:4]]
                    constraint = []
                    if len(words) > 4:
                        constraint = [int(c) for c in words[4:7]]
                    self.optConstraints.append(numpy.array(constraint, dtype = int))
                    coords = self.lattice.diffpy().fractional(numpy.array(coords[0:3])*a_0)
                    self.structure.addNewAtom(atomSymbol, xyz = numpy.array(coords[0:3]))
    
    
    def setStructureFromQEInput(self):
        """ Loads structure from PWSCF config file"""
        self.atomicSpecies = OrderedDict()
        self.lattice.setLatticeFromPWInput(self.qeConf)
        #self.lattice = QELattice(qeConf = self.qeConf)
        self.structure = Structure(lattice = self.lattice.diffpy())
        self.nat = self.ntyp = None
        self.filename = self.qeConf.filename
        self.optConstraints = []
        
        if 'system' in self.qeConf.namelists:        
            self.nat  = int(self.qeConf.namelist('system').param('nat'))
            self.ntyp  = int(self.qeConf.namelist('system').param('ntyp'))
        if 'atomic_positions' in self.qeConf.cards:        
            atomicLines = self.qeConf.card('atomic_positions').lines()
            self.atomicPositionsType = self.qeConf.card('atomic_positions').arg()
            if self.atomicPositionsType == 'bohr' or self.atomicPositionsType == 'angstrom':
                raise NotImplementedError
            if self.atomicPositionsType == None:
                self.atomicPositionsType = 'alat'
            for line in atomicLines:
                if '!' not in line:
                    words = line.split()
                    coords = [float(w) for w in words[1:4]]
                    constraint = []
                    if len(words) > 4:
                        constraint = [int(c) for c in words[4:7]]
                    self.optConstraints.append(numpy.array(constraint, dtype = int))
                    atomSymbol = words[0]
                    if self.atomicPositionsType == 'alat':
                        coords = self.lattice.diffpy().fractional(numpy.array(coords[0:3])*self.lattice.a0)
                    if self.atomicPositionsType == 'crystal':
                        coords = numpy.array(coords[0:3])
                    self.structure.addNewAtom(atomSymbol, xyz = numpy.array(coords[0:3]))
        # parse mass ATOMIC_SPECIES section:
         
        if 'atomic_species' in self.qeConf.cards:
            atomicSpeciesLines = self.qeConf.card('atomic_species').lines()
            for line in atomicSpeciesLines:
                if '!' not in line:
                    if line.strip() != '':                            
                        atomicSpeciesWords = line.split()
                        element = atomicSpeciesWords[0]
                        mass = 0
                        ps = ''
                        if len(atomicSpeciesWords) > 1 :
                            mass = float(atomicSpeciesWords[1])
                        if len(atomicSpeciesWords) > 2:
                            ps = atomicSpeciesWords[2]
                        self.atomicSpecies[element] =  AtomicSpecies(element, mass, ps)


    def load(self, source, **args):
        task = {
            'diffpy': self.setStructureFromDiffpyStructure
        }
        if source == 'diffpy':
            if 'ibrav' in args and args['ibrav'] != 0:
                task['diffpy'] = self.setReducedStructureFromDiffpyStructure

        task[source](**args)
        
        self.updatePWInput(qeConf = self.qeConf)


    def setStructureFromDiffpyStructure(self, structure, massList = [], psList = [], ibrav = 0):
        """
        structure - diffpy.Structure object
        ibrav - Lattice index
        psList - list of strings with pseudopotential names
        diffpyStructure object will be modified with reduced atomic positions
        """      
        diffpyLattice = structure.lattice
        
        self.structure = structure
               
        
        #set lattice and  convert to bohr units
        qeLattice = QELattice(ibrav = 0, a = 1.889725989, base = diffpyLattice.base)
        
        
        self.lattice = qeLattice
        self.lattice.type = 'generic cubic'

        atomNames = []
        for a in structure:
            if self._element(a) not in atomNames:
                atomNames.append(self._element(a))
        
        #print atomNames
        #print len(massList)
        for i, elem in enumerate(atomNames):
            if len(massList) - 1 < i:
                mass = 0
            else:
                mass = massList[i]
            if len(psList) - 1 < i:
                ps = ''
            else:
                ps = psList[i]               
            self.atomicSpecies[elem] =  AtomicSpecies(elem, mass, ps)
        
        for atom in structure:
            self.optConstraints.append([])        
        
        # for i, atom in enumerate(structure):
            # elem = self._element(atom)
            # if len(massList) - 1 < i:
                # mass = 0
            # else:
                # mass = massList[i]
            # if len(psList) - 1 < i:
                # ps = ''
            # else:
                # ps = psList[i]
            # self.atomicSpecies[elem] =  AtomicSpecies(elem, mass, ps)
            # self.optConstraints.append([])

#        for atom, mass, ps in zip(structure, massList, psList):
#            elem = self._element(atom)
#            self.atomicSpecies[elem] =  AtomicSpecies(elem, mass, ps)
#            self.optConstraints.append([])

        self.nat = len(structure)
        self.ntyp = len(self.atomicSpecies)        
     
                        
    def setReducedStructureFromDiffpyStructure(self, structure, ibrav, massList = [], psList = []):
        """
        structure - diffpy.Structure object
        ibrav - Lattice index
        psList - list of strings with pseudopotential names
        diffpyStructure object will be modified with reduced atomic positions
        """

        #self.atomicSpecies = OrderedDict()
        #self.optConstraints = []
        #self.atomicPositionsType = 'crystal'

        diffpyLattice = structure.lattice

        a = diffpyLattice.a
        b = diffpyLattice.b
        c = diffpyLattice.c
        cAB = cosd(diffpyLattice.gamma)
        cBC = cosd(diffpyLattice.alpha)
        cAC = cosd(diffpyLattice.beta)

        qeLattice = QELattice(ibrav = ibrav, a = a, b = b, c = c,  cBC =  cBC, \
                              cAC = cAC, cAB = cAB)

        self.lattice = qeLattice
        # make a deep copy (does not wok now)
        #reducedStructure = Structure(diffpyStructure)
        reducedStructure = structure

        reducedStructure.placeInLattice(Lattice(base=qeLattice.diffpy().base))

        # collect atoms that are at equivalent position to some previous atom
        duplicates = set([a1
            for i0, a0 in enumerate(reducedStructure) for a1 in reducedStructure[i0+1:]
                if   self._element(a0) == self._element(a1) and equalPositions(a0.xyz, a1.xyz, eps=1e-4)])

        
        # Filter out duplicate atoms.  Use slice assignment so that
        # reducedStructure is not replaced with a list.
        reducedStructure[:] = [a for a in reducedStructure if not a in duplicates]

        self.structure = reducedStructure

        atomNames = []
        for a in reducedStructure:
            if self._element(a) not in atomNames:
                atomNames.append(self._element(a))
        
        #print atomNames
        #print len(massList)
        for i, elem in enumerate(atomNames):
            if len(massList) - 1 < i:
                mass = 0
            else:
                mass = massList[i]
            if len(psList) - 1 < i:
                ps = ''
            else:
                ps = psList[i]      
            #print mass, ps
            # atomDict[a] = 
        # for i, atom in enumerate(reducedStructure):
            # elem = self._element(atom)
            # if len(massList) - 1 < i:
                # mass = 0
            # else:
                # mass = massList[i]
            # if len(psList) - 1 < i:
                # ps = ''
            # else:
                # ps = psList[i]            
            self.atomicSpecies[elem] =  AtomicSpecies(elem, mass, ps)
        
        for atom in reducedStructure:
            self.optConstraints.append([])

        # convert to bohr units
        self.lattice.setLattice(ibrav, self.lattice.a*1.889725989, \
                                 self.lattice.b*1.889725989,
                                 self.lattice.c*1.889725989)

        self.nat = len(reducedStructure)
        self.ntyp = len(self.atomicSpecies)

        # use rstrip to avoid duplicate line feed
        #print reducedStructure.writeStr(format='discus').rstrip()
        #print reducedStructure.writeStr(format='discus')
        #print reducedStructure.writeStr().rstrip()
        #print reducedStructure
        #self.lattice = setLatticeFromDiffpyLattice(structure.lattice, ibrav)


    # def toString(self):
        # s = self.lattice.toString() + '\n'
        # if self.atomicPositionsType == 'alat':
            # s = s + 'Atomic positions in units of lattice parametr "a":\n'        
        # if self.atomicPositionsType == 'crystal':
            # s = s + 'Atomic positions in crystal coordinates:\n'
        # for atom, constraint in zip(self.structure, self.optConstraints):
            # if self.atomicPositionsType == 'alat':      
                # coords = self.lattice.diffpy().cartesian(atom.xyz)/self.lattice.a
                # coords = self.formatString%(coords[0], coords[1], coords[2])
            # else:
                # if self.atomicPositionsType == 'crystal':
                    # coords = self.formatString%(atom.xyz[0], atom.xyz[1], atom.xyz[2])
                # else:
                    # raise NonImplementedError
            # s = s + '%-3s'%self._element(atom) + '    ' + coords + '  ' \
                    # + str(constraint)[1:-1] + '\n'

        # s = s + '\n'
        # for element, specie in self.atomicSpecies.items():
            # s = s + specie.toString() + '\n'

        # return s

    def toString(self, string = None):
        if string != None:
            string = self.lattice.toString(string = string)
            qeConf = QEInput(config = string)
            qeConf.parse()
        else:
            if self.qeConf != None:
                qeConf = self.qeConf
            else:
                qeConf = QEInput(config = '')

        self.updatePWInput(qeConf)
        return qeConf.toString()

        
    def updatePWInput(self, qeConf = None):

        if qeConf == None:
            qeConf = self.qeConf

        self.lattice.updatePWInput(qeConf)

        if 'system' not in qeConf.namelists:
            qeConf.addNamelist('system')            
        qeConf.namelist('system').remove('ntyp')
        qeConf.namelist('system').remove('nat')
        if self.ntyp != None:
            qeConf.namelist('system').add('ntyp', self.ntyp)
        if self.nat != None:
            qeConf.namelist('system').add('nat', self.nat)
        
        if len(qeConf.namelist('system').params) == 0:
            qeConf.removeNamelist('system')  

        if 'atomic_positions' in qeConf.cards:
            qeConf.removeCard('atomic_positions')
        qeConf.createCard('atomic_positions')
        qeConf.card('atomic_positions').setArg(self.atomicPositionsType)
        for atom, constraint in zip(self.structure, self.optConstraints):
            if self.atomicPositionsType == 'alat':
                coords = self.lattice.diffpy().cartesian(atom.xyz)/self.lattice.a
                coords = self.formatString%(coords[0], coords[1], coords[2])
            else:
                if self.atomicPositionsType == 'crystal':
                    coords = self.formatString%(atom.xyz[0], atom.xyz[1], atom.xyz[2])
                else:
                    raise NonImplementedError
            line = '%-3s'%self._element(atom) + '    ' + coords + '  ' + str(constraint)[1:-1]
            qeConf.card('atomic_positions').addLine(line)
        
        if len(qeConf.card('atomic_positions').lines()) == 0:
            qeConf.removeCard('atomic_positions')

        # update ATOMIC_SPECIES card
        if 'atomic_species' in qeConf.cards:
            qeConf.removeCard('atomic_species')
        qeConf.createCard('atomic_species')
        for element, specie in self.atomicSpecies.items():
            qeConf.card('atomic_species').addLine(specie.toString())
        
        if len(qeConf.card('atomic_species').lines()) == 0:
            qeConf.removeCard('atomic_species')        


    def save(self, fname = None):
        """Writes/updates structure into PW config file,
           if the file does not exist, new one will be created"""
        filename = fname
        if fname != None:
            self.lattice.save(filename)
            qeConf = QEInput(fname)
            qeConf.parse()
        else:
            filename = self.filename
            self.lattice.save(filename)
            qeConf = self.qeConf
            
        self.updatePWInput(qeConf)
        qeConf.save(filename)

        

    def diffpy(self):
        return self.structure


    def _element(self, atom):
        """
        Is needed for suport both diffpy and matter classess
        """
        if 'element' in dir(atom):
            return atom.element
        else:
            if 'symbol' in dir(atom):
                return atom.symbol
            else:
                raise
Ejemplo n.º 22
0
class QEStructure():
    def __init__(self, qeConf):
        """the structure is initialized from PWSCF config file
           'lattice' and 'structure' are automatically updated"""
        self.filename = qeConf.filename
        self.atomicSpecies = OrderedDict()
        self.formatString = '%# .8f %# .8f %# .8f'
        # optConstraints three 1/0 for each coordinate of each atom
        self.optConstraints = []
        self.qeConf = qeConf
        self.lattice = QELattice()
        self.structure = Structure(lattice=self.lattice.diffpy())
        self.nat = None
        self.ntyp = None

    def parseInput(self):
        self.setStructureFromQEInput()

    def parseOutput(self, pwscfOutputFile):
        self.setStructureFromPWOutput(pwscfOutputFile)

    def setStructureFromPWOutput(self, pwscfOutputFile):
        """
        Loads structure from PWSCF output file. If there was geometry
        optimization (relax or vc-relax), the structure will be reinitialized
        from the last step of the optimization
        """
        file = open(pwscfOutputFile)
        pwscfOut = file.readlines()
        pseudoList = []
        atomList = []
        massList = []
        self.atomicSpecies = OrderedDict()
        self.atomicPositionsType = 'alat'
        # parse beginning:
        for i, line in enumerate(pwscfOut):
            if 'lattice parameter (a_0)' in line:
                a_0 = float(line.split()[4])
            if 'bravais-lattice index' in line:
                ibrav = int(line.split('=')[1])
            if 'number of atoms/cell' in line:
                self.nat = int(line.split('=')[1])
            if 'number of atomic types' in line:
                self.ntyp = int(line.split('=')[1])
            if 'PseudoPot.' in line:
                pseudoList.append(line.split('read from file')[1].strip())
            if 'atomic species   valence    mass     pseudopotential' in line:
                for j in range(self.ntyp):
                    atomList.append(pwscfOut[i + j + 1].split()[0])
                    massList.append(float(pwscfOut[i + j + 1].split()[2]))
            if 'crystal axes: (cart. coord. in units of a_0)' in line:
                latticeVectors = [
                    [float(f) * a_0 for f in pwscfOut[i + 1].split()[3:6]],
                    [float(f) * a_0 for f in pwscfOut[i + 2].split()[3:6]],
                    [float(f) * a_0 for f in pwscfOut[i + 3].split()[3:6]]
                ]
                self.lattice.setLatticeFromQEVectors(ibrav, latticeVectors)
            if 'site n.     atom                  positions (a_0 units)' in line:
                self.structure = Structure(lattice=self.lattice.diffpy())
                for n in range(self.nat):
                    words = pwscfOut[i + n + 1].split()
                    atomSymbol = words[1]
                    coords = [float(w) for w in words[6:9]]
                    constraint = []
                    self.optConstraints.append(
                        numpy.array(constraint, dtype=int))
                    #print numpy.array(coords[0:3])*a_0
                    coords = self.lattice.diffpy().fractional(
                        numpy.array(coords[0:3]) * a_0)
                    self.structure.addNewAtom(atomSymbol,
                                              xyz=numpy.array(coords[0:3]))

        for a, m, p in zip(atomList, massList, pseudoList):
            self.atomicSpecies[a] = AtomicSpecies(a, m, p)

        #print 'Input structure from output file: ', self.toString()
        #Parse end:
        # Find all geometry optimization steps
        posList = [
            i for i, line in enumerate(pwscfOut) if '!    total energy' in line
        ]
        lastSection = pwscfOut[posList[-1]:]
        for i, line in enumerate(lastSection):
            if 'CELL_PARAMETERS (alat)' in line:
                latticeVectors = [
                    [float(f) * a_0 for f in lastSection[i + 1].split()],
                    [float(f) * a_0 for f in lastSection[i + 2].split()],
                    [float(f) * a_0 for f in lastSection[i + 3].split()]
                ]
                self.lattice.setLatticeFromQEVectors(ibrav, latticeVectors)
                #self.lattice = QELattice(ibrav = ibrav, base = latticeVectors)
                print self.lattice.diffpy().base
            if 'ATOMIC_POSITIONS (alat)' in line:
                self.structure = Structure(lattice=self.lattice.diffpy())
                for n in range(self.nat):
                    words = lastSection[i + n + 1].split()
                    atomSymbol = words[0]
                    coords = [float(w) for w in words[1:4]]
                    constraint = []
                    if len(words) > 4:
                        constraint = [int(c) for c in words[4:7]]
                    self.optConstraints.append(
                        numpy.array(constraint, dtype=int))
                    coords = self.lattice.diffpy().fractional(
                        numpy.array(coords[0:3]) * a_0)
                    self.structure.addNewAtom(atomSymbol,
                                              xyz=numpy.array(coords[0:3]))

    def setStructureFromQEInput(self):
        """ Loads structure from PWSCF config file"""
        self.atomicSpecies = OrderedDict()
        self.lattice.setLatticeFromPWInput(self.qeConf)
        #self.lattice = QELattice(qeConf = self.qeConf)
        self.structure = Structure(lattice=self.lattice.diffpy())
        self.nat = int(self.qeConf.namelist('system').param('nat'))
        self.ntyp = int(self.qeConf.namelist('system').param('ntyp'))
        atomicLines = self.qeConf.card('atomic_positions').lines()
        self.atomicPositionsType = self.qeConf.card('atomic_positions').arg()
        if self.atomicPositionsType == 'bohr' or self.atomicPositionsType == 'angstrom':
            raise NotImplementedError
        if self.atomicPositionsType == None:
            self.atomicPositionsType = 'alat'
        for line in atomicLines:
            if '!' not in line:
                words = line.split()
                coords = [float(w) for w in words[1:4]]
                constraint = []
                if len(words) > 4:
                    constraint = [int(c) for c in words[4:7]]
                self.optConstraints.append(numpy.array(constraint, dtype=int))
                atomSymbol = words[0]
                if self.atomicPositionsType == 'alat':
                    coords = self.lattice.diffpy().fractional(
                        numpy.array(coords[0:3]) * self.lattice.a0)
                if self.atomicPositionsType == 'crystal':
                    coords = numpy.array(coords[0:3])
                self.structure.addNewAtom(atomSymbol,
                                          xyz=numpy.array(coords[0:3]))
                # parse mass ATOMIC_SPECIES section:
                atomicSpeciesLines = self.qeConf.card('atomic_species').lines()
                for line in atomicSpeciesLines:
                    if '!' not in line:
                        atomicSpeciesWords = line.split()
                        element = atomicSpeciesWords[0]
                        mass = float(atomicSpeciesWords[1])
                        ps = atomicSpeciesWords[2]
                        self.atomicSpecies[element] = AtomicSpecies(
                            element, mass, ps)

    def toString(self):
        s = self.lattice.toString() + '\n'
        if self.atomicPositionsType == 'alat':
            s = s + 'Atomic positions in units of lattice parametr "a":\n'
        if self.atomicPositionsType == 'crystal':
            s = s + 'Atomic positions in crystal coordinates:\n'
        for atom, constraint in zip(self.structure, self.optConstraints):
            if self.atomicPositionsType == 'alat':
                coords = self.lattice.diffpy().cartesian(
                    atom.xyz) / self.lattice.a
                coords = self.formatString % (coords[0], coords[1], coords[2])
            else:
                if self.atomicPositionsType == 'crystal':
                    coords = self.formatString % (v[0], v[1], v[2]) % (
                        atom.xyz[0], atom.xyz[1], atom.xyz[2])
                else:
                    raise NonImplementedError
            s = s + '%-3s'%atom.element + '    ' + coords + '  ' \
                    + str(constraint)[1:-1] + '\n'

        s = s + '\n'
        for element, specie in self.atomicSpecies.items():
            s = s + specie.toString() + '\n'

        return s

    def updatePWInput(self, qeConf=None):

        if qeConf == None:
            qeConf = self.qeConf

        self.lattice.updatePWInput(qeConf)

        qeConf.namelist('system').remove('ntyp')
        qeConf.namelist('system').remove('nat')
        qeConf.namelist('system').add('ntyp', self.ntyp)
        qeConf.namelist('system').add('nat', self.nat)

        if 'atomic_positions' in qeConf.cards:
            qeConf.removeCard('atomic_positions')
        qeConf.createCard('atomic_positions')
        qeConf.card('atomic_positions').setArg(self.atomicPositionsType)
        for atom, constraint in zip(self.structure, self.optConstraints):
            if self.atomicPositionsType == 'alat':
                coords = self.lattice.diffpy().cartesian(
                    atom.xyz) / self.lattice.a
                coords = self.formatString % (coords[0], coords[1], coords[2])
            else:
                if self.atomicPositionsType == 'crystal':
                    coords = self.formatString % (atom.xyz[0], atom.xyz[1],
                                                  atom.xyz[2])
                else:
                    raise NonImplementedError
            line = '%-3s' % atom.element + '    ' + coords + '  ' + str(
                constraint)[1:-1]
            qeConf.card('atomic_positions').addLine(line)

        # update ATOMIC_SPECIES card
        if 'atomic_species' in qeConf.cards:
            qeConf.removeCard('atomic_species')
        qeConf.createCard('atomic_species')
        for element, specie in self.atomicSpecies.items():
            qeConf.card('atomic_species').addLine(specie.toString())

    def save(self, fname=None):
        """Writes/updates structure into PW config file,
           if the file does not exist, new one will be created"""
        if fname != None:
            filename = fname
            self.lattice.save(filename)
            qeConf = QEInput(fname)
            qeConf.parse()
        else:
            filename = self.filename
            self.lattice.save(filename)
            qeConf = self.qeConf
        self.updatePWInput(qeConf)

        qeConf.save(filename)

    def diffpy(self):
        return self.structure
Ejemplo n.º 23
0
    def setStructureFromPWOutput(self, pwscfOutputFile): 
        """
        Loads structure from PWSCF output file. If there was geometry
        optimization (relax or vc-relax), the structure will be reinitialized
        from the last step of the optimization
        """
        file = open(pwscfOutputFile)
        pwscfOut = file.readlines()
        pseudoList = []
        atomList = []
        massList = []
        self.atomicSpecies = OrderedDict()
        self.atomicPositionsType = 'alat'  
        # parse beginning:
        for i, line in enumerate(pwscfOut):
            if 'lattice parameter (a_0)' in line:
                a_0 = float(line.split()[4])
            if 'bravais-lattice index' in line:
                ibrav = int(line.split('=')[1])
            if 'number of atoms/cell' in line:
                self.nat = int(line.split('=')[1])
            if 'number of atomic types' in line:
                self.ntyp = int(line.split('=')[1])
            if 'PseudoPot.' in line:
                pseudoList.append(line.split('read from file')[1].strip())
            if 'atomic species   valence    mass     pseudopotential' in line:
                for j in range(self.ntyp):
                    atomList.append(pwscfOut[i+j+1].split()[0])
                    massList.append(float(pwscfOut[i+j+1].split()[2]))
            if 'crystal axes: (cart. coord. in units of a_0)' in line:
                latticeVectors = [[float(f)*a_0 for f in pwscfOut[i + 1].split()[3:6] ],
                                  [float(f)*a_0 for f in pwscfOut[i + 2].split()[3:6] ],
                                  [float(f)*a_0 for f in pwscfOut[i + 3].split()[3:6] ]]
                self.lattice = QELattice(ibrav = ibrav, base = latticeVectors)
            if 'site n.     atom                  positions (a_0 units)' in line:
                self.structure = Structure(lattice = self.lattice.matter())
                for n in range(self.nat):
                    words = pwscfOut[i + n + 1].split()
                    atomSymbol = words[1]
                    coords = [float(w) for w in words[6:9]]
                    constraint = []
                    self.optConstraints.append(numpy.array(constraint, dtype = int))
                    #print numpy.array(coords[0:3])*a_0
                    coords = self.lattice.matter().fractional(numpy.array(coords[0:3])*a_0)
                    self.structure.addNewAtom(atomSymbol, xyz = numpy.array(coords[0:3]))

        for a, m, p in zip(atomList, massList, pseudoList):
            self.atomicSpecies[a] = AtomicSpecies(a, m, p)

        #print 'Input structure from output file: ', self.toString()
        #Parse end:
        # Find all geometry optimization steps
        posList =  [i for i,line in enumerate(pwscfOut) if '!    total energy' in line]
        lastSection = pwscfOut[posList[-1]:]
        for i, line in enumerate(lastSection):
            if 'CELL_PARAMETERS (alat)' in line:
                latticeVectors = [[float(f)*a_0 for f in lastSection[i + 1].split() ],
                                  [float(f)*a_0 for f in lastSection[i + 2].split() ],
                                  [float(f)*a_0 for f in lastSection[i + 3].split() ]]
                self.lattice = QELattice(ibrav = ibrav, base = latticeVectors)
                print self.lattice.matter().base
            if 'ATOMIC_POSITIONS (alat)' in line:
                self.structure = Structure(lattice = self.lattice.matter())
                for n in range(self.nat):
                    words = lastSection[i + n + 1].split()
                    atomSymbol = words[0]
                    coords = [float(w) for w in words[1:4]]
                    constraint = []
                    if len(words) > 4:
                        constraint = [int(c) for c in words[4:7]]
                    self.optConstraints.append(numpy.array(constraint, dtype = int))
                    #print numpy.array(coords[0:3])*a_0
                    coords = self.lattice.matter().fractional(numpy.array(coords[0:3])*a_0)
                    self.structure.addNewAtom(atomSymbol, xyz = numpy.array(coords[0:3]))
Ejemplo n.º 24
0
class QEStructure():
    
    def __init__(self, qeConf):
        """the structure is initialized from PWSCF config file
           'lattice' and 'structure' are automatically updated"""
        self.filename = qeConf.filename
        self.atomicSpecies = OrderedDict()
        self.formatString = '%# .8f %# .8f %# .8f'
        # optConstraints three 1/0 for each coordinate of each atom
        self.optConstraints = []
        self.qeConf = qeConf
        #self.qeConf.parse()
        #self.setStructureFromQEInput()
        self.lattice = None
        self.structure = None
        self.nat = None
        self.ntyp = None
        
        
    def parseInput(self):
        self.setStructureFromQEInput()
        
    def parseOutput(self, pwscfOutputFile):
        self.setStructureFromPWOutput(pwscfOutputFile)

#    def _parseAtomicPositions(self, pwscfOut):
#        for n in range(self.nat):
#            words = pwscfOut[i + n + 1].split()
#            atomSymbol = words[0]
#            coords = [float(w) for w in words[1:4]]
#            constraint = []
#            if len(words) > 4:
#                constraint = [int(c) for c in words[4:7]]
#            self.optConstraints.append(numpy.array(constraint, dtype = int))
#            print numpy.array(coords[0:3])*a_0
#            coords = self.lattice.matter().fractional(numpy.array(coords[0:3])*a_0)
#            self.structure.addNewAtom(atomSymbol, xyz = numpy.array(coords[0:3]))

    def setStructureFromPWOutput(self, pwscfOutputFile): 
        """
        Loads structure from PWSCF output file. If there was geometry
        optimization (relax or vc-relax), the structure will be reinitialized
        from the last step of the optimization
        """
        file = open(pwscfOutputFile)
        pwscfOut = file.readlines()
        pseudoList = []
        atomList = []
        massList = []
        self.atomicSpecies = OrderedDict()
        self.atomicPositionsType = 'alat'  
        # parse beginning:
        for i, line in enumerate(pwscfOut):
            if 'lattice parameter (a_0)' in line:
                a_0 = float(line.split()[4])
            if 'bravais-lattice index' in line:
                ibrav = int(line.split('=')[1])
            if 'number of atoms/cell' in line:
                self.nat = int(line.split('=')[1])
            if 'number of atomic types' in line:
                self.ntyp = int(line.split('=')[1])
            if 'PseudoPot.' in line:
                pseudoList.append(line.split('read from file')[1].strip())
            if 'atomic species   valence    mass     pseudopotential' in line:
                for j in range(self.ntyp):
                    atomList.append(pwscfOut[i+j+1].split()[0])
                    massList.append(float(pwscfOut[i+j+1].split()[2]))
            if 'crystal axes: (cart. coord. in units of a_0)' in line:
                latticeVectors = [[float(f)*a_0 for f in pwscfOut[i + 1].split()[3:6] ],
                                  [float(f)*a_0 for f in pwscfOut[i + 2].split()[3:6] ],
                                  [float(f)*a_0 for f in pwscfOut[i + 3].split()[3:6] ]]
                self.lattice = QELattice(ibrav = ibrav, base = latticeVectors)
            if 'site n.     atom                  positions (a_0 units)' in line:
                self.structure = Structure(lattice = self.lattice.matter())
                for n in range(self.nat):
                    words = pwscfOut[i + n + 1].split()
                    atomSymbol = words[1]
                    coords = [float(w) for w in words[6:9]]
                    constraint = []
                    self.optConstraints.append(numpy.array(constraint, dtype = int))
                    #print numpy.array(coords[0:3])*a_0
                    coords = self.lattice.matter().fractional(numpy.array(coords[0:3])*a_0)
                    self.structure.addNewAtom(atomSymbol, xyz = numpy.array(coords[0:3]))

        for a, m, p in zip(atomList, massList, pseudoList):
            self.atomicSpecies[a] = AtomicSpecies(a, m, p)

        #print 'Input structure from output file: ', self.toString()
        #Parse end:
        # Find all geometry optimization steps
        posList =  [i for i,line in enumerate(pwscfOut) if '!    total energy' in line]
        lastSection = pwscfOut[posList[-1]:]
        for i, line in enumerate(lastSection):
            if 'CELL_PARAMETERS (alat)' in line:
                latticeVectors = [[float(f)*a_0 for f in lastSection[i + 1].split() ],
                                  [float(f)*a_0 for f in lastSection[i + 2].split() ],
                                  [float(f)*a_0 for f in lastSection[i + 3].split() ]]
                self.lattice = QELattice(ibrav = ibrav, base = latticeVectors)
                print self.lattice.matter().base
            if 'ATOMIC_POSITIONS (alat)' in line:
                self.structure = Structure(lattice = self.lattice.matter())
                for n in range(self.nat):
                    words = lastSection[i + n + 1].split()
                    atomSymbol = words[0]
                    coords = [float(w) for w in words[1:4]]
                    constraint = []
                    if len(words) > 4:
                        constraint = [int(c) for c in words[4:7]]
                    self.optConstraints.append(numpy.array(constraint, dtype = int))
                    #print numpy.array(coords[0:3])*a_0
                    coords = self.lattice.matter().fractional(numpy.array(coords[0:3])*a_0)
                    self.structure.addNewAtom(atomSymbol, xyz = numpy.array(coords[0:3]))
                #self.lattice.ibrav = ibrav
        #print 'Output structure from output file: ', self.toString()
    
    
    def setStructureFromQEInput(self):
        """ Loads structure from PWSCF config file"""
        self.atomicSpecies = OrderedDict()
        self.lattice = QELattice(qeConf = self.qeConf)
        self.structure = Structure(lattice = self.lattice.matter())
        self.nat  = int(self.qeConf.namelist('system').param('nat'))
        self.ntyp  = int(self.qeConf.namelist('system').param('ntyp'))
        atomicLines = self.qeConf.card('atomic_positions').lines()
        self.atomicPositionsType = self.qeConf.card('atomic_positions').arg()
        if self.atomicPositionsType == 'bohr' or self.atomicPositionsType == 'angstrom':
            raise NotImplementedError
        if self.atomicPositionsType == None:
            self.atomicPositionsType = 'alat'
        for line in atomicLines:
            if '!' not in line:
                words = line.split()
                coords = [float(w) for w in words[1:4]]
                constraint = []
                if len(words) > 4:
                    constraint = [int(c) for c in words[4:7]]
                self.optConstraints.append(numpy.array(constraint, dtype = int))
                atomSymbol = words[0]
                if self.atomicPositionsType == 'alat':
                    coords = self.lattice.matter().fractional(numpy.array(coords[0:3])*self.lattice.a0)
                if self.atomicPositionsType == 'crystal':
                    coords = numpy.array(coords[0:3])
                self.structure.addNewAtom(atomSymbol, xyz = numpy.array(coords[0:3]))
                # parse mass ATOMIC_SPECIES section:
                atomicSpeciesLines = self.qeConf.card('atomic_species').lines()
                for line in atomicSpeciesLines:
                    if '!' not in line:
                        atomicSpeciesWords = line.split()
                        element = atomicSpeciesWords[0]
                        mass = float(atomicSpeciesWords[1])
                        ps = atomicSpeciesWords[2]
                        self.atomicSpecies[element] =  AtomicSpecies(element, mass, ps)


    def toString(self):
        s = self.lattice.toString() + '\n'
        if self.atomicPositionsType == 'alat':
            s = s + 'Atomic positions in units of lattice parametr "a":\n'        
        if self.atomicPositionsType == 'crystal':
            s = s + 'Atomic positions in crystal coordinates:\n'
        for atom, constraint in zip(self.structure, self.optConstraints):
            if self.atomicPositionsType == 'alat':      
                coords = self.lattice.matter().cartesian(atom.xyz)/self.lattice.a
                coords = self.formatString%(coords[0], coords[1], coords[2])
                #coords = str(coords/self.lattice.a)[1:-1]
            else:
                if self.atomicPositionsType == 'crystal':
                    #coords = str(atom.xyz)[1:-1]
                    coords = self.formatString%(v[0], v[1], v[2])%(atom.xyz[0], atom.xyz[1], atom.xyz[2])
                else:
                    raise NonImplementedError
            s = s + '%-3s'%atom.element + '    ' + coords + '  ' \
                    + str(constraint)[1:-1] + '\n'

        s = s + '\n'
        for element, specie in self.atomicSpecies.items():
            s = s + specie.toString() + '\n'

        return s


    def updatePWInput(self, qeConf = None):

        if qeConf == None:
            qeConf = self.qeConf

        self.lattice.updatePWInput(qeConf)

        qeConf.namelist('system').remove('ntyp')
        qeConf.namelist('system').remove('nat')
        qeConf.namelist('system').add('ntyp', self.ntyp)
        qeConf.namelist('system').add('nat', self.nat)

        if 'atomic_positions' in qeConf.cards:
            qeConf.removeCard('atomic_positions')
        qeConf.createCard('atomic_positions')
        qeConf.card('atomic_positions').setArg(self.atomicPositionsType)
        for atom, constraint in zip(self.structure, self.optConstraints):
            if self.atomicPositionsType == 'alat':
                coords = self.lattice.matter().cartesian(atom.xyz)/self.lattice.a
                coords = self.formatString%(coords[0], coords[1], coords[2])
            else:
                if self.atomicPositionsType == 'crystal':
                    #coords = str(atom.xyz)[1:-1]
                    coords = self.formatString%(atom.xyz[0], atom.xyz[1], atom.xyz[2])
                else:
                    raise NonImplementedError
            line = '%-3s'%atom.element + '    ' + coords + '  ' + str(constraint)[1:-1]
#            line = atom.element + ' ' + coords + ' ' + str(constraint)[1:-1]
            qeConf.card('atomic_positions').addLine(line)

        # update ATOMIC_SPECIES card
        if 'atomic_species' in qeConf.cards:
            qeConf.removeCard('atomic_species')
        qeConf.createCard('atomic_species')
        for element, specie in self.atomicSpecies.items():
            qeConf.card('atomic_species').addLine(specie.toString())


    def save(self, fname = None):
        """Writes/updates structure into PW config file,
           if the file does not exist, new one will be created"""
        if fname != None:
            filename = fname
            self.lattice.save(filename)
            qeConf = QEInput(fname)
            qeConf.parse()
        else:
            filename = self.filename
            self.lattice.save(filename)
            qeConf = self.qeConf
            #qeConf.parse()
        self.updatePWInput(qeConf )
            
        qeConf.save(filename)

    def diffpy(self):
        return self.structure
Ejemplo n.º 25
0
class QEAtom(object):
    """Atom --> class for storing atom information

    Data members:
        element     -- type of the atom
        
        xyz         -- fractional coordinates
        
        name        -- atom label
        
        xyz_cartn   -- absolute Cartesian coordinates, property synced with xyz
        
        lattice     -- coordinate system for fractional coordinates,
                       an instance of Lattice or None for Cartesian system
                       

    """


    def __init__(self, atype=None, xyz=None, mass = None,  \
                 potential = None, lattice=None, optConstraint = None, name=None):
        """Create atom of a specified type at given lattice coordinates.
        Atom(a) creates a copy of Atom instance a.

        atype         -- element symbol string or Atom instance
        xyz           -- fractional(crystal) coordinates
        name          -- atom label
        mass          -- atom mass
        potential     -- pseudopotential file name
        lattice       -- QE coordinate system for fractional coordinates
        optConstraint -- list of up to three constraints for each coordinate 
                         for QE geometry optimization (0 or 1)
        """
        # declare data members
        self.element = None
        self._xyz = numpy.zeros(3, dtype=float)
        self.name = ''
        self._mass = 0
        self._potential = ''
        self.lattice = QELattice()
        from pwinput import PWInput
        self.lattice._qeInput = PWInput()
        self._optConstraint = numpy.array([], dtype = int)
        # assign them as needed
        if isinstance(atype, QEAtom):
            atype_dup = atype.__copy__()
            self.__dict__.update(atype_dup.__dict__)
        else:
            self.element = atype
        # take care of remaining arguments
        if xyz is not None:             self._xyz[:] = xyz
        if name is not None:            self.name = name
        if mass is not None:            self._mass = mass
        if potential is not None:       self._potential = potential
        if lattice is not None:         self.lattice = lattice
        if optConstraint is not None:  self._optConstraint = \
                                        numpy.array(optConstraint, dtype = int)
        return


    def __repr__(self):
        """simple string representation"""
        xyz = self.xyz
        s = "%-4s %8.6f %8.6f %8.6f %8.6f %s" % \
                (self.element, xyz[0], xyz[1], xyz[2], self.mass, self.potential)
        return s

    def __copy__(self):
        """Return a copy of this instance.
        """
        adup = QEAtom(self.element)
        adup.__dict__.update(self.__dict__)
        # create copies for what should be copied
        adup.xyz = numpy.array(self.xyz)
        return adup

    ####################################################################
    # property handlers
    ####################################################################

    # xyz_cartn

    def _get_xyz_cartn(self):
        if not self.lattice:
            rv = self.xyz
        else:
            rv = CartesianCoordinatesArray(self.lattice, self.xyz)
        return rv

    def _set_xyz_cartn(self, value):
        if not self.lattice:
            self.xyz[:] = value
        else:
            self.xyz = self.lattice.fractional(value)
            self.lattice._qeInput.update()
        return

    xyz_cartn = property(_get_xyz_cartn, _set_xyz_cartn, doc =
        """absolute Cartesian coordinates of an atom
        """ )

    
    def _get_xyz(self):
        return self._xyz
    
    def _set_xyz(self, value):
        self._xyz = value
        self.lattice._qeInput.update()

    xyz = property(_get_xyz, _set_xyz, doc =
        """fractional coordinates of an atom
        """ )
    
    
    def _get_mass(self):
        return self._mass
    
    def _set_mass(self, value):
        self._mass = value
        self.lattice._qeInput.update()
        
    mass = property(_get_mass, _set_mass, doc =
        """mass of an atom """ ) 
    

    def _get_potential(self):
        return self._potential
    
    def _set_potential(self, value):
        self._potential = value
        self.lattice._qeInput.update()
        
    potential = property(_get_potential, _set_potential, doc =
        """property. Name of paseudopotential of an atom """)
    
    
    def _get_optConstraint(self):
        return self._optConstraint
    
    def _set_optConstraint(self, value):
        self._optConstraints = value
        self.lattice._qeInput.update()
        
    optConstraint = property(_get_optConstraint, _set_optConstraint, doc =
        """optimization constraint, e.g. [1, 0, 1] of an atom for QE geometry  
optimization""")
Ejemplo n.º 26
0
    def _setReducedStructureFromDiffpyStructure(self, structure, ibrav, massList = [], psList = []):
        """
        structure - diffpy.Structure object
        ibrav - Lattice index
        psList - list of strings with potential names
        diffpyStructure object will be modified with reduced atomic positions
        """
        import copy

        diffpyLattice = copy.deepcopy(structure.lattice)

        a = diffpyLattice.a
        b = diffpyLattice.b
        c = diffpyLattice.c
        cAB = cosd(diffpyLattice.gamma)
        cBC = cosd(diffpyLattice.alpha)
        cAC = cosd(diffpyLattice.beta)

        qeLattice = QELattice(ibrav = ibrav, a = a, b = b, c = c,  cBC =  cBC, \
                              cAC = cAC, cAB = cAB)

        qeLattice._qeInput = self._qeInput
        self.lattice = qeLattice
        # make a deep copy:
        reducedStructure = Structure(atoms = structure)

        reducedStructure.placeInLattice(Lattice(base=qeLattice.diffpy().base))

        # collect atoms that are at equivalent position to some previous atom
        duplicates = set([a1
            for i0, a0 in enumerate(reducedStructure) for a1 in reducedStructure[i0+1:]
                if   self._element(a0) == self._element(a1) and equalPositions(a0.xyz, a1.xyz, eps=1e-4)])

        
        # Filter out duplicate atoms.  Use slice assignment so that
        # reducedStructure is not replaced with a list.
        reducedStructure[:] = [a for a in reducedStructure if not a in duplicates]

        atomNames = []
        for a in reducedStructure:
            if self._element(a) not in atomNames:
                atomNames.append(self._element(a))
        
        atomicSpecies = {}
        for i, elem in enumerate(atomNames):
            if len(massList) - 1 < i:
                mass = 0
            else:
                mass = massList[i]
            if len(psList) - 1 < i:
                ps = ''
            else:
                ps = psList[i]
            atomicSpecies[elem] =  (mass, ps)
               
        self[:] = []
        
        # convert to bohr units
        self.lattice.setLattice(ibrav, self.lattice.a*1.889725989, \
                                 self.lattice.b*1.889725989,
                                 self.lattice.c*1.889725989)

        for atom in reducedStructure:
            elem = self._element(atom)
            self.addNewAtom(atype = elem, xyz = atom.xyz, \
                            mass = atomicSpecies[elem][0], \
                            potential = atomicSpecies[elem][1],\
                            lattice = self.lattice, optConstraint = [])
Ejemplo n.º 27
0
class QEStructure( Structure ):
    """ 
    Parses and handles Quantum Espresso structure information. 
    QEStructure is inherited from a diffpy.Structure, which is in turn 
    inherited from a Python list. 
    QEStructure is a list of QEAtom object instances.
    All list functionality is preserved.  setitem and 
    setslice methods are overloaded so that the lattice attribute 
    of atoms get set to lattice.
       
    QEStructure, QELattice and QEAtom objects  contain a hidden QEInput 
    pointer to current Quantum Espresso parsing object. 
    if QEInput.autoUpdate = True (default),any change in a property from
    QEStructure, QELattice, or QEAtom will automatically invoke 
    QEInput.update(). In that case, QEInput.save() or QEInput.toString() will
    immediately yield updated QE input file or a string
       
    All properties are mutually synchronized. E.g. change in a lattice parameter
    will also affect other lattice parameters as well as atomic positions  
    according to the lattice type (ibrav)
    
    
    All relevant methods from diffpy.Structure are redefined.
    
    
    QEStructure read only properties, automatically synchronized with current 
    list of Atoms:
      
    nat                  -- "number of atoms" (integer)
    
    ntyp                 -- "number of atomic types" (integer)
    
    atomicSpecies        -- OrderedDic of AtomicSpecies class instances 
                            (see AtomicSpecies class definition)
                           
    Other properties:
    
    atomicPositionsType  -- 'crystal' (default), 'alat', 'bohr', and 'angstrom'
         
                           
    Note: This version of QEStructure does not support multiple images from 
    QE input files
    
    """  
    def __init__(self, atoms = [], lattice = None, filename = None, qeInput = None):
        """
        atoms        -- list of QEAtom atom instances or a QEStructure object
        lattice      -- QELattice object
        filename     -- filename QE input file 
        qeInput      -- pointer to a PWInput parsing object. If not None, 
                        its PWInput.structure and PWInput.structure.lattice 
                        will be  reset to the current instance of the structure       
        """
          
        Structure.__init__(self) 
        self.formatString = '%# .8f %# .8f %# .8f'
        self._atomicPositionsType = 'crystal'
        self._qeInput = qeInput
        self.lattice = QELattice()
        self.lattice._qeInput = self._qeInput
           
        if lattice != None:           
            self.lattice = QELattice( lattice = lattice )
            self._qeInput = self.lattice._qeInput           
           
        # CP and PW inputs are compatible
        from pwinput import PWInput
        from cpinput import CPInput        
        if isinstance( atoms, PWInput) or isinstance( atoms, CPInput):
            qeInput = atoms        
        elif isinstance( atoms, QEStructure):
            stru = atoms                
            self.__dict__.update( stru.__dict__ )            
            # deep copy of the lattice will deep copy PWInput as well
            self.lattice = QELattice(lattice = stru.lattice)            
            self._qeInput = self.lattice._qeInput                
            self[:] = stru
        else:
            if self.lattice == None:
                raise "Lattice must be provided"                            
            self[:] = atoms
        
        if filename != None:        
            qeInput = PWInput(filename = filename)
            qeInput.parse()
            self.parseInput(qeInput)            
        
        if qeInput != None:            
            self.lattice._qeInput = qeInput
            self._qeInput = qeInput
        
        if self.lattice._qeInput != None:
            self.lattice._qeInput.structure = self
            self.lattice._qeInput.structure.lattice = self.lattice
            
        return
                                                        
         
    def addNewAtom(self, *args, **kwargs):
        """Add new Atom instance to the end of this Structure.

        All arguments are forwarded to Atom constructor.

        No return value.
        """
        kwargs['lattice'] = self.lattice
        a = QEAtom(*args, **kwargs)
        list.append(self, a)
        self._uncache('labels')
        return
    

    def placeInLattice(self, new_lattice):
        """place structure into new_lattice coordinate system

        sets lattice to new_lattice and recalculate fractional coordinates
        of all atoms so their absolute positionls remain the same

        return self
        """
        Tx = numpy.dot(self.lattice.diffpy().base, new_lattice.diffpy().recbase)
        Tu = numpy.dot(self.lattice.diffpy().normbase, new_lattice.diffpy().recnormbase)
        for a in self:
            a.xyz = numpy.dot(a.xyz, Tx)
        tmpInput = self.lattice._qeInput
        self.lattice = new_lattice
        self.lattice._qeInput = tmpInput
        self.lattice._qeInput.structure = self        
        return self
        

    ##############################################################################
    # overloaded list methods - taken from diffpy.Structure
    ##############################################################################

    def append(self, a, copy=True):
        """Append atom to a structure and update its lattice attribute.

        a    -- instance of QEAtom
        copy -- flag for appending a copy of a.
                When False, append a and update a.owner.

        No return value.
        """
        self._uncache('labels')
        adup = copy and QEAtom(a) or a
        adup.lattice = self.lattice
        list.append(self, adup)
        if hasattr(self, '_qeInput') and  self._qeInput != None:
            self._qeInput.update()
        return

    def insert(self, idx, a, copy=True):
        """Insert atom a before position idx in this Structure.

        idx  -- position in atom list
        a    -- instance of QEAtom
        copy -- flag for inserting a copy of a.
                When False, append a and update a.lattice.

        No return value.
        """
        self._uncache('labels')
        adup = copy and QEAtom(a) or a
        adup.lattice = self.lattice
        list.insert(self, idx, adup)
        if hasattr(self, '_qeInput') and  self._qeInput != None:
            self._qeInput.update()
        return

    def extend(self, atoms, copy=True):
        """Extend Structure by appending copies from a list of atoms.

        atoms -- list of QEAtom instances
        copy  -- flag for extending with copies of QEAtom instances.
                 When False extend with atoms and update their lattice
                 attributes.

        No return value.
        """
        self._uncache('labels')
        if copy:    adups = [QEAtom(a) for a in atoms]
        else:       adups = atoms
        for a in adups: a.lattice = self.lattice
        list.extend(self, adups)
        if hasattr(self, '_qeInput') and  self._qeInput != None:
            self._qeInput.update()
        return

    def __setitem__(self, idx, a, copy=True):
        """Set idx-th atom to a.

        idx  -- index of atom in this Structure
        a    -- instance of QEAtom
        copy -- flag for setting to a copy of a.
                When False, set to a and update a.lattice.

        No return value.
        """
        self._uncache('labels')
        adup = copy and QEAtom(a) or a
        adup.lattice = self.lattice
        list.__setitem__(self, idx, adup)
        if hasattr(self, '_qeInput') and  self._qeInput != None:
            self._qeInput.update()
        return

    def __setslice__(self, lo, hi, atoms, copy=True):
        """Set Structure slice from lo to hi-1 to the sequence of atoms.

        lo    -- low index for the slice
        hi    -- high index of the slice
        atoms -- sequence of Atom instances
        copy  -- flag for using copies of Atom instances.  When False, set
                 to existing instances and update their lattice attributes.

        No return value.
        """
        self._uncache('labels')
        if copy:    adups = [QEAtom(a) for a in atoms]
        else:       adups = atoms
        for a in adups: a.lattice = self.lattice
        list.__setslice__(self, lo, hi, adups)
        if hasattr(self, '_qeInput') and  self._qeInput != None:
            self._qeInput.update()
        return
    
            
    def _get_nat(self):
        return len(self)
    nat = property(_get_nat, doc ="number of atoms")
    
 
    def _get_ntyp(self):
        return len(self.atomicSpecies)
    
    ntyp = property(_get_ntyp, doc ="number of types")
    
    
    def _get_atomicPositionsType(self):
        return self._atomicPositionsType

    def _set_atomicPositionsType(self, value):
        self._atomicPositionsType = value
        self.lattice._qeInput.update()

    atomicPositionsType = property(_get_atomicPositionsType, \
                                   _set_atomicPositionsType, \
                           doc ="type of atomic positions (crystal, alat ...)")    


    def _get_atomicSpecies(self):
        atomicSpecies = OrderedDict()
        for a in self:
            atomicSpecies[a.element] = AtomicSpecies(element = a.element, \
                                        mass = a.mass, potential = a.potential)
        
        return atomicSpecies

    atomicSpecies = property(_get_atomicSpecies, \
              doc ="returns an ordered dictionary with atomic species' objects")   
               

    def __str__(self):
        """simple string representation"""        
        s = str(self.lattice) + '\n'
        if self.atomicPositionsType == 'alat':
            s = s + 'Atomic positions in units of lattice parametr "a":\n'        
        if self.atomicPositionsType == 'crystal':
            s = s + 'Atomic positions in crystal coordinates:\n'
        for atom in self:
            constraint = atom.optConstraint
            if self.atomicPositionsType == 'alat':
                coords = self.lattice.diffpy().cartesian(atom.xyz)/self.lattice.a
                coords = self.formatString%(coords[0], coords[1], coords[2])
            else:
                if self.atomicPositionsType == 'crystal':
                    coords = self.formatString%(atom.xyz[0], atom.xyz[1], atom.xyz[2])
                else:
                    raise NonImplementedError
            s = s + '%-3s'%self._element(atom) + '    ' + coords + '  ' \
                    + str(constraint)[1:-1] + '\n'
        s = s + '\n'
        
        for element, specie in self.atomicSpecies.items():
            s = s + specie.toString() + '\n'
            
        return s


    def atomLabels(self):
        labels = []
        for l in self.atomicSpecies:
            labels.append(l)
        return labels
        
    def parseInput(self, qeInput):
        from qecalc.qetask.qeparser.qestructureparser.qestructureparser import QEStructureParser
        new_structure = QEStructureParser(qeInput).parseqeInput()
        self.__Init(new_structure)
    
    
    def __Init(self, structure):
        QEStructure.__init__(self)
        if structure is not None:
            self.__dict__.update(structure.__dict__)
            self.lattice.__dict__.update(structure.lattice.__dict__)
            self[:] = structure     


    def read(self, filename, format = 'pwinput'):
        """Load structure from a file, any original data become lost.
l
        filename -- file to be loaded
        format   -- structure formats
                    'pwinput' ( pw.x input, default), 'pwoutput' (pw.x output),
                    'bratoms', 'cif', 'discus', 'pdb', 'pdffit', 'rawxyz', 
                    'xcfg', 'xyz'

        Return instance of data Parser used to process file.  This
        can be inspected for information related to particular format.
        """        
        from  qecalc.qetask.qeparser.qestructureparser import parser_index      
        if self._qeInput == None:
            from qecalc.qetask.qeparser.pwinput import PWInput         
            self._qeInput = PWInput()
            #self._qeInput.parse()
        
        if format in parser_index:             
            module = __import__("qestructureparser.P_" + format, globals(), \
                                locals(), ['P_' + format], -1)
            parser = module.getParser(self._qeInput)
            new_structure = parser.parse(filename)
        else:            
            diffpyStruct = Structure()
            parser = diffpyStruct.read(filename, format = format)
            new_structure = QEStructure(qeInput = self._qeInput)
            new_structure._setStructureFromDiffpyStructure(diffpyStruct, \
                                        massList = [], psList = [], ibrav = 0)

        new_structure.lattice._qeInput.update( forceUpdate = True )
        self.__Init(new_structure)
        return parser

        
    def readStr(self, s, format = 'pwinput'):
        """Load structure from a string, any original data become lost.

        filename -- file to be loaded
        format   -- structure formats
                    'pwinput' ( pw.x input, default), 'pwoutput' (pw.x output),
                    'bratoms', 'cif', 'discus', 'pdb', 'pdffit', 'rawxyz', 
                    'xcfg', 'xyz'

        Return instance of data Parser used to process file.  This
        can be inspected for information related to particular format.
        """        
        from  qecalc.qetask.qeparser.qestructureparser import parser_index
        from qecalc.qetask.qeparser.pwinput import PWInput
        
        #if self._qeInput == None:            
        #    self._qeInput = PWInput()            
            #self._qeInput.parse()
        
        if format in parser_index:             
            module = __import__("qestructureparser.P_" + format, globals(), \
                                locals(), ['P_' + format], -1)
            parser = module.getParser(self._qeInput)
            new_structure = parser.parseStr(s)            
        else:            
            diffpyStruct = Structure()
            parser = diffpyStruct.readStr(s, format = format)
            new_structure = QEStructure(qeInput = self._qeInput)
            new_structure._setStructureFromDiffpyStructure(diffpyStruct, \
                                        massList = [], psList = [], ibrav = 0)

        new_structure.lattice._qeInput.update( forceUpdate = True )
        #new_structure._qeInput = new_structure.lattice._qeInput
        #print 'toString: ', new_structure._qeInput.toString()
        #self = QEStructure(new_structure)
        self.__Init(new_structure)
        return parser


    def write(self, filename = None, format = "pwinput"):
        """Save structure to file in the specified format
        
        format   -- structure formats
                    'pwinput' ( pw.x input, default), 'pwoutput' (pw.x output),
                    'bratoms', 'cif', 'discus', 'pdb', 'pdffit', 'rawxyz', 
                    'xcfg', 'xyz'        
                    
        No return value.
        """        
        if format == "pwinput":
            self._qeInput.update( qeInput = qeInput, forceUpdate = True )
            if filename == None:
                filename = self._qeInput.filename
            input = QEInput(config = self._qeInput.toString(), type='pw')
            input.save( filename = filename)
        else:
            self.diffpy().write( filename = filename, format = format )                        
        return


    def writeStr(self, format = 'pwinput'):
        """return string representation of the structure in specified format
        
        format   -- structure formats
                    'pwinput' ( pw.x input, default), 'pwoutput' (pw.x output),
                    'bratoms', 'cif', 'discus', 'pdb', 'pdffit', 'rawxyz', 
                    'xcfg', 'xyz'
        """
        
        if format == 'pwinput':
            return self.toString()
        else:
            return self.diffpy().writeStr(format = format)        


    def toString(self, string = None):
        """Writes/updates structure into PW config string.
           If the string is None, one, based on current structure 
           will be generated
           """        
        if string != None:
            stru = QEStructure()
            stru.readStr(string, format = 'pwinput')
            qeInput = stru._qeInput
        else:
            qeInput = self._qeInput

        self._qeInput.update( qeInput = qeInput, forceUpdate = True )
        return qeInput.toString()

 
    def save(self, filename = None):
        """Writes/updates structure into PW config file,
           if the file does not exist, new one will be created"""
        from os.path import exists
        from qecalc.qetask.qeparser.pwinput import PWInput
        if filename != None:
            if not exists(filename):
                f = open(filename, 'w')            
            qeInput = PWInput()
            qeInput.readFile(filename)
        else:
            qeInput = self._qeInput
        self._qeInput.update( qeInput = qeInput, forceUpdate = True )
        qeInput.save()


    def reduce(self, ibrav = None): 
        """
        Reduces a structure instance with nonprimitive lattice ( i.e. solves
        for equivalent atomic positions) according to specified  lattice type (ibrav). 
        Each ibrav number corresponds to a specific primitive lattice 
        (see QE documentation).         
        """
        
        ib = self.lattice.ibrav
        if ibrav != None:
            ib = ibrav            
        
        a = self.lattice.diffpy().a
        b = self.lattice.diffpy().b
        c = self.lattice.diffpy().c
        cAB = cosd(self.lattice.diffpy().gamma)
        cBC = cosd(self.lattice.diffpy().alpha)
        cAC = cosd(self.lattice.diffpy().beta)
        if ib > 0:
            qeLattice = QELattice(ibrav = ib, a = a, b = b, c = c,  cBC =  cBC, \
                              cAC = cAC, cAB = cAB)
        else:
            qeLattice = QELattice(ibrav = ib, base = self.lattice.base)            
     
        qeLattice._qeInput = self._qeInput
        
        reducedStructure = QEStructure(self)
        
        reducedStructure.placeInLattice( qeLattice )

        # collect atoms that are at equivalent position to some previous atom
        duplicates = set([a1
            for i0, a0 in enumerate(reducedStructure) for a1 in reducedStructure[i0+1:]
                if  a0.element == a1.element and equalPositions(a0.xyz, a1.xyz, eps=1e-4)])

        
        # Filter out duplicate atoms.  Use slice assignment so that
        # reducedStructure is not replaced with a list.
        self.lattice = qeLattice
        self[:] = [a for a in reducedStructure if not a in duplicates]
        
        return

    def load(self, source, **args):
        task = {
            'diffpy': self._setStructureFromDiffpyStructure,
            'matter': self._setStructureFromMatter,
        }

        if source == 'matter':
            if 'ibrav' in args and args['ibrav'] != 0:
                task['matter'] = self._setReducedStructureFromMatter
        
        if source == 'diffpy':
            if 'ibrav' in args and args['ibrav'] != 0:
                task['diffpy'] = self._setReducedStructureFromDiffpyStructure

        task[source](**args)
        
        self.lattice._qeInput.update()



    def _matter_diffpy(self, structure):
        """
        converts matter Structure object to diffpy.Structure
        returns diffpy.Structure object
        """
        l = structure.lattice
        lat = Lattice( a=l.a, b=l.b, c=l.c, alpha=l.alpha, \
                        beta=l.beta, gamma=l.gamma)
        stru = Structure( lattice  = lat)
        for a in structure:
            stru.addNewAtom(atype = a.symbol, xyz = a.xyz, name = a.symbol, \
                       anisotropy=a.anisotropy, U=a.U, Uisoequiv=a.Uisoequiv, \
                       lattice=lat)        
        return stru
                    

    def _setStructureFromMatter(self, structure, massList = [], psList = [], ibrav = 0):
        
        stru = self._matter_diffpy( structure )
        self._setStructureFromDiffpyStructure(structure=stru,\
                                  massList=massList, psList=psList,ibrav=ibrav)
    
    
    def _setReducedStructureFromMatter(self, structure, ibrav, massList = [], psList = []):
        stru = self._matter_diffpy( structure )
        self._setReducedStructureFromDiffpyStructure(structure = stru, \
                                 ibrav=ibrav, massList=massList,psList=psList) 
        

    def _setStructureFromDiffpyStructure(self, structure, massList = [], psList = [], ibrav = 0):
        """
        structure - diffpy.Structure object
        ibrav - Lattice index
        psList - list of strings with potential names
        diffpyStructure object will be modified with reduced atomic positions
        """      
        diffpyLattice = structure.lattice
            
        qeLattice = QELattice(ibrav = 0, base = diffpyLattice.base)
        qeLattice.a = 1.889725989*qeLattice.a
        qeLattice._qeInput = self._qeInput
        
        self.lattice = qeLattice
        self.lattice.type = 'generic cubic'

        atomNames = []        
        for a in structure:
            if self._element(a) not in atomNames:
                atomNames.append(self._element(a))
        
        atomicSpecies = {}
        for i, elem in enumerate(atomNames):
            if len(massList) - 1 < i:
                mass = 0
            else:
                mass = massList[i]
            if len(psList) - 1 < i:
                ps = ''
            else:
                ps = psList[i]               
            atomicSpecies[elem] =  (mass, ps)   
        
        self[:] = []
        for atom in structure:
            elem = self._element(atom)
            self.addNewAtom(atype = elem, xyz = atom.xyz, \
                            mass = atomicSpecies[elem][0], \
                            potential = atomicSpecies[elem][1],\
                            lattice = self.lattice, optConstraint = [])             
             
                        
    def _setReducedStructureFromDiffpyStructure(self, structure, ibrav, massList = [], psList = []):
        """
        structure - diffpy.Structure object
        ibrav - Lattice index
        psList - list of strings with potential names
        diffpyStructure object will be modified with reduced atomic positions
        """
        import copy

        diffpyLattice = copy.deepcopy(structure.lattice)

        a = diffpyLattice.a
        b = diffpyLattice.b
        c = diffpyLattice.c
        cAB = cosd(diffpyLattice.gamma)
        cBC = cosd(diffpyLattice.alpha)
        cAC = cosd(diffpyLattice.beta)

        qeLattice = QELattice(ibrav = ibrav, a = a, b = b, c = c,  cBC =  cBC, \
                              cAC = cAC, cAB = cAB)

        qeLattice._qeInput = self._qeInput
        self.lattice = qeLattice
        # make a deep copy:
        reducedStructure = Structure(atoms = structure)

        reducedStructure.placeInLattice(Lattice(base=qeLattice.diffpy().base))

        # collect atoms that are at equivalent position to some previous atom
        duplicates = set([a1
            for i0, a0 in enumerate(reducedStructure) for a1 in reducedStructure[i0+1:]
                if   self._element(a0) == self._element(a1) and equalPositions(a0.xyz, a1.xyz, eps=1e-4)])

        
        # Filter out duplicate atoms.  Use slice assignment so that
        # reducedStructure is not replaced with a list.
        reducedStructure[:] = [a for a in reducedStructure if not a in duplicates]

        atomNames = []
        for a in reducedStructure:
            if self._element(a) not in atomNames:
                atomNames.append(self._element(a))
        
        atomicSpecies = {}
        for i, elem in enumerate(atomNames):
            if len(massList) - 1 < i:
                mass = 0
            else:
                mass = massList[i]
            if len(psList) - 1 < i:
                ps = ''
            else:
                ps = psList[i]
            atomicSpecies[elem] =  (mass, ps)
               
        self[:] = []
        
        # convert to bohr units
        self.lattice.setLattice(ibrav, self.lattice.a*1.889725989, \
                                 self.lattice.b*1.889725989,
                                 self.lattice.c*1.889725989)

        for atom in reducedStructure:
            elem = self._element(atom)
            self.addNewAtom(atype = elem, xyz = atom.xyz, \
                            mass = atomicSpecies[elem][0], \
                            potential = atomicSpecies[elem][1],\
                            lattice = self.lattice, optConstraint = [])
    
    
    def updatePWInput(self, qeInput = None):
        """
        Deprecated
        """
        self._qeInput.update()

    def diffpy(self):
        stru = Structure(lattice = self.lattice.diffpy())
        for atom in self:
            stru.addNewAtom(atype = atom.element, xyz = atom.xyz, \
                                              lattice = self.lattice.diffpy() )
        return stru


    def _element(self, atom):
        """
        Is needed for support both diffpy and matter classes
        """
        if 'element' in dir(atom):
            return atom.element
        else:
            if 'symbol' in dir(atom):
                return atom.symbol
            else:
                raise
Ejemplo n.º 28
0
class QEStructure():
    def __init__(self, fname):
        """the structure is initialized from PWSCF config file
           'lattice' and 'structure' are automatically updated"""
        self.filename = fname
        self.atomicSpecies = OrderedDict()
        self.lattice = None
        # optConstraints three 1/0 for each coordinate of each atom
        self.optConstraints = []
        self.qeConf = QEConfig(fname)
        self.qeConf.parse()
        self.setStructureFromPWSCF()

    def setStructureFromPWSCF(self):
        """ Loads structure from PWSCF config file"""
        self.lattice = QELattice(fname=self.filename)
        self.structure = Structure(lattice=self.lattice.diffpy())
        self.nat = int(self.qeConf.namelist('system').param('nat'))
        self.ntyp = int(self.qeConf.namelist('system').param('ntyp'))
        atomicLines = self.qeConf.card('atomic_positions').getLines()
        self.atomicPositionsType = self.qeConf.card(
            'atomic_positions').argument()
        if self.atomicPositionsType == 'bohr' or self.atomicPositionsType == 'angstrom':
            raise NotImplementedError
        if self.atomicPositionsType == None:
            self.atomicPositionsType = 'alat'
        for line in atomicLines:
            if '!' not in line:
                words = line.split()
                coords = [float(w) for w in words[1:4]]
                constraint = []
                if len(words) > 4:
                    constraint = [int(c) for c in words[4:7]]
                self.optConstraints.append(numpy.array(constraint, dtype=int))
                atomSymbol = words[0]
                if self.atomicPositionsType == 'alat':
                    coords = self.lattice.diffpy().fractional(
                        numpy.array(coords[0:3]) * self.lattice.a0)
                if self.atomicPositionsType == 'crystal':
                    coords = numpy.array(coords[0:3])
                self.structure.addNewAtom(atomSymbol,
                                          xyz=numpy.array(coords[0:3]))
                # parse mass ATOMIC_SPECIES section:
                atomicSpeciesLines = self.qeConf.card(
                    'atomic_species').getLines()
                for line in atomicSpeciesLines:
                    if '!' not in line:
                        atomicSpeciesWords = line.split()
                        element = atomicSpeciesWords[0]
                        mass = float(atomicSpeciesWords[1])
                        ps = atomicSpeciesWords[2]
                        self.atomicSpecies[element] = AtomicSpecies(
                            element, mass, ps)

    def saveStructureToPWSCF(self, fname=None):
        """Writes/updates structure into PWSCF config file,
           if the file does not exist, new one will be created"""
        if fname != None:
            filename = fname
            self.lattice.saveLatticeToPWSCF(filename)
            qeConf = QEConfig(fname)
            qeConf.parse()
        else:
            filename = self.filename
            self.lattice.saveLatticeToPWSCF(filename)
            qeConf = self.qeConf
            qeConf.parse()

        qeConf.namelist('system').removeParam('ntyp')
        qeConf.namelist('system').removeParam('nat')
        qeConf.namelist('system').addParam('ntyp', self.ntyp)
        qeConf.namelist('system').addParam('nat', self.nat)

        if 'atomic_positions' in qeConf.cards:
            qeConf.removeCard('atomic_positions')
        qeConf.createCard('atomic_positions')
        qeConf.card('atomic_positions').setArgument(self.atomicPositionsType)
        for atom, constraint in zip(self.structure, self.optConstraints):
            if self.atomicPositionsType == 'alat':
                coords = self.lattice.diffpy().cartesian(atom.xyz)
                coords = str(coords / self.lattice.a)[1:-1]
            else:
                if self.atomicPositionsType == 'crystal':
                    #coords = str(atom.xyz)[1:-1]
                    coords = '%f.8  %f.8  %f.8' % (atom.xyz[0], atom.xyz[1],
                                                   atom.xyz[2])
                else:
                    raise NonImplementedError
            line = atom.element + '    ' + coords + '  ' + str(
                constraint)[1:-1]
            #            line = atom.element + ' ' + coords + ' ' + str(constraint)[1:-1]
            qeConf.card('atomic_positions').addLine(line)

        # update ATOMIC_SPECIES card
        if 'atomic_species' in qeConf.cards:
            qeConf.removeCard('atomic_species')
        qeConf.createCard('atomic_species')
        for element, specie in self.atomicSpecies.items():
            qeConf.card('atomic_species').addLine(specie.toString())

        qeConf.save(filename)

    def diffpy(self):
        return self.structure