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 = [])
Beispiel #2
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
Beispiel #3
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 = [])
Beispiel #4
0
class QEStructure(Structure):
    """ 
    Parses and handles Quantum Espresso structure information. 
    QEStructure is inherited from a matter.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 matter.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

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

    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.matter().base,
                       new_lattice.matter().recbase)
        Tu = numpy.dot(self.lattice.matter().normbase,
                       new_lattice.matter().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.symbol] = AtomicSpecies(symbol = a.symbol, \
                                        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.matter().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 NotImplementedError
            s = s + '%-3s'%self._symbol(atom) + '    ' + coords + '  ' \
                    + str(constraint)[1:-1] + '\n'
        s = s + '\n'
        for symbol, 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.
        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:
            struct = Structure()
            parser = struct.read(filename, format=format)
            new_structure = QEStructure(qeInput=self._qeInput)
            new_structure._setStructureFromMatterStructure(struct, \
                                        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:
            #print 'cannot use format '+format
            struct = Structure()
            parser = struct.readStr(s, format=format)
            new_structure = QEStructure(qeInput=self._qeInput)
            new_structure._setStructureFromMatterStructure(struct, \
                                        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.matter().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.matter().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.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

    def load(self, **args):
        #def load(self, source, **args):
        if 'ibrav' in args and args['ibrav'] != 0:
            self._setReducedStructureFromMatterStructure(**args)
        else:
            self._setStructureFromMatterStructure(**args)
#        task = {
#            'diffpy': self._setStructureFromDiffpyStructure,
#            'matter': self._setStructureFromMatterStructure,
#        }
#        if source == 'matter':
#            if 'ibrav' in args and args['ibrav'] != 0:
#                task['matter'] = self._setReducedStructureFromMatterStructure
#        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 _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 = [])

#    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._symbol(a) not in atomNames:
#                atomNames.append(self._symbol(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._symbol(atom)
#            self.addNewAtom(atype = elem, xyz = atom.xyz, \
#                            mass = atomicSpecies[elem][0], \
#                            potential = atomicSpecies[elem][1],\
#                            lattice = self.lattice, optConstraint = [])

    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 = [])


#    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.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   self._symbol(a0) == self._symbol(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._symbol(a) not in atomNames:
#                atomNames.append(self._symbol(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._symbol(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 matter(self):
        stru = Structure(lattice=self.lattice.matter())
        for atom in self:
            stru.addNewAtom(atype = atom.symbol, xyz = atom.xyz, \
                                              lattice = self.lattice.matter() )
        return stru

    def _symbol(self, atom):
        """
        Is needed for support both diffpy and matter classes
        """
        if 'symbol' in dir(atom):
            return atom.symbol
        else:
            if 'symbol' in dir(atom):
                return atom.symbol
            else:
                raise
class QEStructure( Structure ):
    """ 
    Parses and handles Quantum Espresso structure information. 
    QEStructure is inherited from a matter.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 matter.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
                                                        
    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')

    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.matter().base, new_lattice.matter().recbase)
        Tu = numpy.dot(self.lattice.matter().normbase, new_lattice.matter().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.symbol] = AtomicSpecies(symbol = a.symbol, \
                                        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.matter().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 NotImplementedError
            s = s + '%-3s'%self._symbol(atom) + '    ' + coords + '  ' \
                    + str(constraint)[1:-1] + '\n'
        s = s + '\n'
        for symbol, 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.
        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:            
            struct = Structure()
            parser = struct.read(filename, format = format)
            new_structure = QEStructure(qeInput = self._qeInput)
            new_structure._setStructureFromMatterStructure(struct, \
                                        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:    
            #print 'cannot use format '+format        
            struct = Structure()
            parser = struct.readStr(s, format = format)
            new_structure = QEStructure(qeInput = self._qeInput)
            new_structure._setStructureFromMatterStructure(struct, \
                                        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.matter().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.matter().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.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

    def load(self, **args):
    #def load(self, source, **args):
        if 'ibrav' in args and args['ibrav'] != 0:
            self._setReducedStructureFromMatterStructure(**args)
        else:
            self._setStructureFromMatterStructure(**args)
#        task = {
#            'diffpy': self._setStructureFromDiffpyStructure,
#            'matter': self._setStructureFromMatterStructure,
#        }
#        if source == 'matter':
#            if 'ibrav' in args and args['ibrav'] != 0:
#                task['matter'] = self._setReducedStructureFromMatterStructure
#        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 _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 = [])  
        
#    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._symbol(a) not in atomNames:
#                atomNames.append(self._symbol(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._symbol(atom)
#            self.addNewAtom(atype = elem, xyz = atom.xyz, \
#                            mass = atomicSpecies[elem][0], \
#                            potential = atomicSpecies[elem][1],\
#                            lattice = self.lattice, optConstraint = [])                          
     
    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 = [])
                        
#    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.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   self._symbol(a0) == self._symbol(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._symbol(a) not in atomNames:
#                atomNames.append(self._symbol(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._symbol(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 matter(self):
        stru = Structure(lattice = self.lattice.matter())
        for atom in self:
            stru.addNewAtom(atype = atom.symbol, xyz = atom.xyz, \
                                              lattice = self.lattice.matter() )
        return stru


    def _symbol(self, atom):
        """
        Is needed for support both diffpy and matter classes
        """
        if 'symbol' in dir(atom):
            return atom.symbol
        else:
            if 'symbol' in dir(atom):
                return atom.symbol
            else:
                raise
Beispiel #6
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