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 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]
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 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 __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 _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 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 __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 _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 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)
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 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]))