def __init__(self, ibrav=1, a=1., b=1., c=1., cBC=0., cAC=0., cAB=0., qeConf=None, base=None): # Lattice.__init__(self) self.formatString = '%# .8f %# .8f %# .8f' self.qeConf = qeConf self._type = 'celldm' self._primitiveLattice = Lattice() self._standardLattice = Lattice() self._base = None self._a0 = None if self.qeConf != None: self.setLatticeFromPWInput(self.qeConf) else: if ibrav > 0 and base != None: self.setLatticeFromQEVectors(ibrav, base) else: self.setLattice(ibrav, a, b, c, cBC, cAC, cAB, base)
def __init__(self, ibrav = 1,a = 1. ,b = 1.,c = 1., cBC = 0.,cAC = 0. ,cAB = 0., base = None, lattice = None ): self.formatString = '%# .8f %# .8f %# .8f' self._qeInput = None #self._qeInput = None self._type = 'celldm' # diffpyStructure container class, used for lattice operations self.__primitiveLattice = Lattice() # Lattice vectors in bohr or angstrom: self._base = None # initialize the lattice if there is enough information if ibrav > 0 and base != None: self.setLatticeFromQEVectors(ibrav, base) else: self.setLattice(ibrav ,a ,b , c, cBC ,cAC ,cAB, base) # copy constructor: if isinstance(ibrav, QELattice) or lattice != None: if lattice.ibrav > 0: self.setLattice( ibrav = lattice.ibrav, a = lattice.a, \ b = lattice.b, c = lattice.c, cBC = lattice.cBC, cAC = lattice.cAC,\ cAB = lattice.cAB) else: self.setLattice( ibrav = lattice.ibrav, a = lattice.a, \ base = lattice.base ) # copy input: from pwinput import PWInput self._qeInput = PWInput() self._qeInput.readString( lattice._qeInput.toString() )
def __init__(self, atoms=[], lattice=None, title=None, filename=None, format=None): """define group of atoms in a specified lattice. atoms -- list of Atom instances to be included in this Structure. When atoms argument is an existing Structure instance, the new Structure is its copy. lattice -- instance of Lattice defining coordinate systems, property. title -- string description of the structure filename -- optional, name of a file to load the structure from. Overrides atoms argument when specified. format -- optional structure format of the loaded filename. By default all structure formats are tried one by one. Ignored when filename has not been specified. Structure(stru) create a copy of Structure instance stru. Because Structure is inherited from a list it can use list expansions, for example: oxygen_atoms = [ for a in stru if a.element == "O" ] oxygen_stru = Structure(oxygen_atoms, lattice=stru.lattice) """ # if filename is specified load it and return if filename is not None: if any((atoms, lattice, title)): emsg = "Cannot use filename and atoms arguments together." raise ValueError(emsg) readkwargs = (format is not None) and {'format': format} or {} self.read(filename, **readkwargs) return # copy initialization, must be first to allow lattice, title override if isinstance(atoms, Structure): Structure.__copy__(atoms, self) # assign arguments: if title is not None: self.title = title if lattice is not None: self.lattice = lattice elif self.lattice is None: self.lattice = Lattice() # insert atoms unless already done by __copy__ if not len(self) and len(atoms): self.extend(atoms) return
def doc2diffpy(doc): """ Convert doc into diffpy Structure object. """ from numpy import asarray from diffpy.Structure.atom import Atom from diffpy.Structure.lattice import Lattice from diffpy.Structure.structure import Structure lattice = Lattice(a=doc['lattice_abc'][0][0], b=doc['lattice_abc'][0][1], c=doc['lattice_abc'][0][2], alpha=doc['lattice_abc'][1][0], beta=doc['lattice_abc'][1][1], gamma=doc['lattice_abc'][1][2]) atoms = [] for ind, atom in enumerate(doc['atom_types']): # encode atype as utf-8 or you will waste hours of your life atoms.append( Atom(atype=atom.encode('utf-8'), xyz=asarray(doc['positions_frac'][ind]))) title = None for sources in doc['source']: if sources.endswith('.res') or sources.endswith('.castep'): title = sources.split('/')[-1].split('.')[0].encode('utf-8') return Structure(atoms, lattice, title=title)
def __init__(self, ibrav = 1,a = 1. ,b = 1.,c = 1., cBC = 0.,cAC = 0. ,cAB = 0., qeConf = None, base = None ): # Lattice.__init__(self) self.formatString = '%# .8f %# .8f %# .8f' self.qeConf = qeConf self._type = 'celldm' self._primitiveLattice = Lattice() self._standardLattice = Lattice() self._base = None self._a0 = None if self.qeConf != None: self.setLatticeFromPWInput(self.qeConf) else: if ibrav > 0 and base != None: self.setLatticeFromQEVectors(ibrav, base) else: self.setLattice(ibrav ,a ,b , c, cBC ,cAC ,cAB, base)
def __init__(self, ibrav = 1,a = 1. ,b = 1.,c = 1., cBC = 0.,cAC = 0. ,cAB = 0., fname = None, base = None ): # Lattice.__init__(self) self.filename = fname self._type = 'celldm' self.qeConf = None # should be none if nothing to parse self._primitiveLattice = Lattice() self._standardLattice = Lattice() self._base = None self._a0 = None if self.filename != None: self.setLatticeFromPWSCF(self.filename) else: if ibrav > 0 and base != None: self.setLatticeFromQEVectors(ibrav, base) else: self.setLattice(ibrav ,a ,b , c, cBC ,cAC ,cAB, base)
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 __init__(self, atoms=[], lattice=None, title=None, filename=None, format=None): """define group of atoms in a specified lattice. atoms -- list of Atom instances to be included in this Structure. When atoms argument is an existing Structure instance, the new Structure is its copy. lattice -- instance of Lattice defining coordinate systems, property. title -- string description of the structure filename -- optional, name of a file to load the structure from. Overrides atoms argument when specified. format -- optional structure format of the loaded filename. By default all structure formats are tried one by one. Ignored when filename has not been specified. Structure(stru) create a copy of Structure instance stru. Because Structure is inherited from a list it can use list expansions, for example: oxygen_atoms = [ for a in stru if a.element == "O" ] oxygen_stru = Structure(oxygen_atoms, lattice=stru.lattice) """ # if filename is specified load it and return if filename is not None: if any((atoms, lattice, title)): emsg = "Cannot use filename and atoms arguments together." raise ValueError(emsg) readkwargs = (format is not None) and {'format' : format} or {} self.read(filename, **readkwargs) return # copy initialization, must be first to allow lattice, title override if isinstance(atoms, Structure): Structure.__copy__(atoms, self) # assign arguments: if title is not None: self.title = title if lattice is not None: self.lattice = lattice elif self.lattice is None: self.lattice = Lattice() # insert atoms unless already done by __copy__ if not len(self) and len(atoms): self.extend(atoms) return
def test_xyz_cartn(self): """check Atom.xyz_cartn property """ hexagonal = Lattice(1, 1, 1, 90, 90, 120) a0 = Atom('C', [0, 0, 0], lattice=hexagonal) a1 = Atom('C', [1, 1, 1], lattice=hexagonal) self.assertTrue(all(a0.xyz_cartn == 0)) rc1 = numpy.array([0.75**0.5, 0.5, 1]) self.assertTrue(numpy.allclose(rc1, a1.xyz_cartn)) a1.xyz_cartn[2] = 0 self.assertTrue(numpy.allclose([1, 1, 0], a1.xyz)) a1.xyz_cartn[:2] = 0 self.assertTrue(all(a1.xyz == 0)) a3 = Atom('C', [1, 2, 3]) self.assertTrue(numpy.array_equal(a3.xyz, a3.xyz_cartn)) a3.xyz_cartn = 1.3 self.assertTrue(all(1.3 == a3.xyz_cartn)) self.assertTrue(all(1.3 == a3.xyz)) return
def __copy__(self, target=None): '''Create a deep copy of this instance. target -- optional target instance for copying, useful for copying a derived class. Defaults to new instance of the same type as self. Return a duplicate instance of this object. ''' if target is None: target = Structure() elif target is self: return target # copy attributes as appropriate: target.title = self.title target.lattice = Lattice(self.lattice) target.pdffit = copy.deepcopy(self.pdffit) # copy all atoms to the target target[:] = self return target
def test_load_matter(self): try: from matter import Structure, Atom, Lattice except ImportError: return at1 = Atom('V', [0., 0., 0.]) at2 = Atom('V', [0.5, 0., 0.]) at3 = Atom('V', [0., 0.5, 0.]) at4 = Atom('V', [0., 0., 0.5]) at5 = Atom('V', [0.5, 0.5, 0.]) at6 = Atom('V', [0., 0.5, 0.5]) at7 = Atom('V', [0.5, 0., 0.5]) at8 = Atom('V', [0.5, 0.5, 0.5]) at9 = Atom('V', [0.25, 0.25, 0.25]) at10 = Atom('Fe', [0.75, 0.25, 0.25]) at11 = Atom('V', [0.75, 0.75, 0.25]) at12 = Atom('Fe', [0.25, 0.75, 0.25]) at13 = Atom('Fe', [0.25, 0.25, 0.75]) at14 = Atom('V', [0.75, 0.25, 0.75]) at15 = Atom('Fe', [0.75, 0.75, 0.75]) at16 = Atom('V', [0.25, 0.75, 0.75]) # set a in angstrom a = 2. * 5.663 / 1.889725989 struct = Structure( [ at1, at2, at3, at4, at5, at6, at7, at8, at9, \ at10, at11, at12, at13, at14, at15, at16], \ lattice = Lattice(a, a, a, 90, 90, 90) ) #print struct massList = [50.9415, 55.847] psList = ['V.pbe-n-van.UPF', 'Fe.pbe-nd-rrkjus.UPF'] self.input.structure.load(source = 'matter', structure = struct, \ ibrav = 2, massList = massList, psList = psList) answer1 = """"Face Centered Cubic" cell: -5.66300000 0.00000000 5.66300000 0.00000000 5.66300000 5.66300000 -5.66300000 5.66300000 0.00000000 Atomic positions in units of lattice parametr "a": V 0.00000000 0.00000000 0.00000000 V 0.50000000 0.00000000 0.00000000 V 0.25000000 0.25000000 0.25000000 Fe 0.75000000 0.25000000 0.25000000 V 50.9415 V.pbe-n-van.UPF Fe 55.8470 Fe.pbe-nd-rrkjus.UPF """ self.assertEqual(str(self.input.structure), answer1) self.input.structure.load(source = 'matter', structure = struct, \ massList = massList, psList = psList) answer2 = """"generic" cell: 11.32600000 0.00000000 0.00000000 0.00000000 11.32600000 0.00000000 0.00000000 0.00000000 11.32600000 Atomic positions in units of lattice parametr "a": V 0.00000000 0.00000000 0.00000000 V 2.99673076 0.00000000 0.00000000 V 0.00000000 2.99673076 0.00000000 V 0.00000000 0.00000000 2.99673076 V 2.99673076 2.99673076 0.00000000 V 0.00000000 2.99673076 2.99673076 V 2.99673076 0.00000000 2.99673076 V 2.99673076 2.99673076 2.99673076 V 1.49836538 1.49836538 1.49836538 Fe 4.49509614 1.49836538 1.49836538 V 4.49509614 4.49509614 1.49836538 Fe 1.49836538 4.49509614 1.49836538 Fe 1.49836538 1.49836538 4.49509614 V 4.49509614 1.49836538 4.49509614 Fe 4.49509614 4.49509614 4.49509614 V 1.49836538 4.49509614 4.49509614 V 50.9415 V.pbe-n-van.UPF Fe 55.8470 Fe.pbe-nd-rrkjus.UPF """ self.assertEqual(str(self.input.structure), answer2)
class Structure(list): """Structure --> group of atoms Structure class is inherited from Python list. It contains a list of Atom instances. Structure overloads setitem and setslice methods so that the lattice attribute of atoms get set to lattice. Data members: title -- structure description lattice -- coordinate system (instance of Lattice) pdffit -- None or a dictionary of PDFFit-related metadata """ # default values for instance attributes title = '' _lattice = None pdffit = None def __init__(self, atoms=[], lattice=None, title=None, filename=None, format=None): """define group of atoms in a specified lattice. atoms -- list of Atom instances to be included in this Structure. When atoms argument is an existing Structure instance, the new Structure is its copy. lattice -- instance of Lattice defining coordinate systems, property. title -- string description of the structure filename -- optional, name of a file to load the structure from. Overrides atoms argument when specified. format -- optional structure format of the loaded filename. By default all structure formats are tried one by one. Ignored when filename has not been specified. Structure(stru) create a copy of Structure instance stru. Because Structure is inherited from a list it can use list expansions, for example: oxygen_atoms = [ for a in stru if a.element == "O" ] oxygen_stru = Structure(oxygen_atoms, lattice=stru.lattice) """ # if filename is specified load it and return if filename is not None: if any((atoms, lattice, title)): emsg = "Cannot use filename and atoms arguments together." raise ValueError(emsg) readkwargs = (format is not None) and {'format': format} or {} self.read(filename, **readkwargs) return # copy initialization, must be first to allow lattice, title override if isinstance(atoms, Structure): Structure.__copy__(atoms, self) # assign arguments: if title is not None: self.title = title if lattice is not None: self.lattice = lattice elif self.lattice is None: self.lattice = Lattice() # insert atoms unless already done by __copy__ if not len(self) and len(atoms): self.extend(atoms) return def __copy__(self, target=None): '''Create a deep copy of this instance. target -- optional target instance for copying, useful for copying a derived class. Defaults to new instance of the same type as self. Return a duplicate instance of this object. ''' if target is None: target = Structure() elif target is self: return target # copy attributes as appropriate: target.title = self.title target.lattice = Lattice(self.lattice) target.pdffit = copy.deepcopy(self.pdffit) # copy all atoms to the target target[:] = self return target def __str__(self): """simple string representation""" s_lattice = "lattice=%s" % self.lattice s_atoms = '\n'.join([str(a) for a in self]) return s_lattice + '\n' + s_atoms 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 = Atom(*args, **kwargs) self.append(a, copy=False) return def getLastAtom(self): """Return Reference to the last Atom in this structure. """ last_atom = self[-1] return last_atom def assignUniqueLabels(self): """Set a unique label string for each atom in this structure. The label strings are formatted as "%(baresymbol)s%(index)i", where baresymbol is the element right-stripped of "[0-9][+-]". No return value. """ elnum = {} # support duplicate atom instances islabeled = set() for a in self: if a in islabeled: continue baresmbl = atomBareSymbol(a.element) elnum[baresmbl] = elnum.get(baresmbl, 0) + 1 a.label = baresmbl + str(elnum[baresmbl]) islabeled.add(a) return def distance(self, aid0, aid1): """Distance between 2 atoms, no periodic boundary conditions. aid0 -- zero based index of the first atom or a string label such as "Na1" aid1 -- zero based index or string label of the second atom. Return float. Raise IndexError for invalid arguments. """ # lookup by labels a0, a1 = self[aid0, aid1] return self.lattice.dist(a0.xyz, a1.xyz) def angle(self, aid0, aid1, aid2): """The bond angle at the second of three atoms in degrees. aid0 -- zero based index of the first atom or a string label such as "Na1" aid1 -- index or string label for the second atom, where the angle is formed aid2 -- index or string label for the third atom Return float. Raise IndexError for invalid arguments. """ a0, a1, a2 = self[aid0, aid1, aid2] u10 = a0.xyz - a1.xyz u12 = a2.xyz - a1.xyz return self.lattice.angle(u10, u12) 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 positions remain the same return self """ Tx = numpy.dot(self.lattice.base, new_lattice.recbase) Tu = numpy.dot(self.lattice.normbase, new_lattice.recnormbase) for a in self: a.xyz = numpy.dot(a.xyz, Tx) if a.anisotropy: a.U = numpy.dot(numpy.transpose(Tu), numpy.dot(a.U, Tu)) self.lattice = new_lattice return self def read(self, filename, format='auto'): """Load structure from a file, any original data become lost. filename -- file to be loaded format -- all structure formats are defined in Parsers submodule, when format == 'auto' all Parsers are tried one by one Return instance of data Parser used to process file. This can be inspected for information related to particular format. """ import diffpy.Structure import diffpy.Structure.Parsers getParser = diffpy.Structure.Parsers.getParser p = getParser(format) new_structure = p.parseFile(filename) # reinitialize data after successful parsing # avoid calling __init__ from a derived class Structure.__init__(self) if new_structure is not None: self.__dict__.update(new_structure.__dict__) self[:] = new_structure if not self.title: import os.path tailname = os.path.basename(filename) tailbase = os.path.splitext(tailname)[0] self.title = tailbase return p def readStr(self, s, format='auto'): """Load structure from a string, any original data become lost. s -- string with structure definition format -- all structure formats are defined in Parsers submodule, when format == 'auto' all Parsers are tried one by one Return instance of data Parser used to process input string. This can be inspected for information related to particular format. """ from diffpy.Structure.Parsers import getParser p = getParser(format) new_structure = p.parse(s) # reinitialize data after successful parsing # avoid calling __init__ from a derived class Structure.__init__(self) if new_structure is not None: self.__dict__.update(new_structure.__dict__) self[:] = new_structure return p def write(self, filename, format): """Save structure to file in the specified format No return value. Note: available structure formats can be obtained by: from Parsers import formats """ from diffpy.Structure.Parsers import getParser p = getParser(format) p.filename = filename s = p.tostring(self) f = open(filename, 'wb') f.write(s) f.close() return def writeStr(self, format): """return string representation of the structure in specified format Note: available structure formats can be obtained by: from Parsers import formats """ from diffpy.Structure.Parsers import getParser p = getParser(format) s = p.tostring(self) return s def tolist(self): '''Return atoms in this Structure as a standard Python list. ''' rv = [a for a in self] return rv # Overloaded list Methods and Operators ---------------------------------- def append(self, a, copy=True): """Append atom to a structure and update its lattice attribute. a -- instance of Atom copy -- flag for appending a copy of a. When False, append a and update a.lattice. No return value. """ adup = copy and Atom(a) or a adup.lattice = self.lattice list.append(self, adup) 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 Atom copy -- flag for inserting a copy of a. When False, append a and update a.lattice. No return value. """ adup = copy and Atom(a) or a adup.lattice = self.lattice list.insert(self, idx, adup) return def extend(self, atoms, copy=True): """Extend Structure by appending copies from a list of atoms. atoms -- list of Atom instances copy -- flag for extending with copies of Atom instances. When False extend with atoms and update their lattice attributes. No return value. """ if copy: adups = [Atom(a) for a in atoms] else: adups = atoms for a in adups: a.lattice = self.lattice list.extend(self, adups) return def __getitem__(self, idx): """Get one or more atoms in this structure. idx -- atom identifier, which can be integer, string or iterable. When integer use standard list lookup. For iterables use numpy lookup, this supports integer or boolean flag arrays. For string or string-containing iterables lookup the atoms by string label. Return an Atom instance for integer or string index or a substructure in all other cases. Raise IndexError for invalid index or for non-unique atom label. Examples: stru[0] --> first atom in the Structure stru[stru.element == 'Na'] --> substructure of all Na atoms stru['Na3'] --> atom with a unique label 'Na3' stru['Na3', 2, 'Cl2'] --> substructure of three atoms, lookup by label is more efficient when done for several atoms at once. """ try: value = list.__getitem__(self, idx) rv = value if type(idx) is slice: rv = self.__emptySharedStructure() rv.extend(value, copy=False) return rv except TypeError: pass # check if there is any string label that should be resolved scalarlabel = type(idx) is str hasstringlabel = scalarlabel or str in map(type, idx) # if not, use numpy indexing to resolve idx if not hasstringlabel: idx1 = idx if type(idx) is tuple: idx1 = numpy.r_[idx] indices = numpy.arange(len(self))[idx1] rhs = [list.__getitem__(self, i) for i in indices] rv = self.__emptySharedStructure() rv.extend(rhs, copy=False) return rv # here we need to resolve at least one string label # build a map of labels to indices and mark duplicate labels duplicate = () labeltoindex = {} for i, a in enumerate(self): labeltoindex[a.label] = (a.label in labeltoindex and duplicate or i) def _resolveindex(aid): aid1 = aid if type(aid) is str: aid1 = labeltoindex.get(aid, None) if aid1 is None: raise IndexError("Invalid atom label %r." % aid) if aid1 is duplicate: raise IndexError("Atom label %r is not unique." % aid) return aid1 # generate new index object that has no strings if scalarlabel: idx2 = _resolveindex(idx) # for iterables preserved the tuple object type else: idx2 = map(_resolveindex, idx) if type(idx) is tuple: idx2 = tuple(idx2) # call this function again and hope there is no recursion loop rv = self[idx2] return rv def __setitem__(self, idx, a, copy=True): """Set idx-th atom to a. idx -- index of atom in this Structure a -- instance of Atom copy -- flag for setting to a copy of a. When False, set to a and update a.lattice. No return value. """ adup = copy and Atom(a) or a adup.lattice = self.lattice list.__setitem__(self, idx, adup) return def __getslice__(self, lo, hi): '''Get a slice of atoms from this Structure. lo, hi -- slice indices, negative values are not supported Return a sub-structure with atom instances in the slice. ''' rv = self.__emptySharedStructure() rv.extend(list.__getslice__(self, lo, hi), copy=False) return rv 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. """ if copy: ownatoms = set(list.__getslice__(self, lo, hi)) adups = [(a in ownatoms and a or Atom(a)) for a in atoms] else: adups = atoms for a in adups: a.lattice = self.lattice list.__setslice__(self, lo, hi, adups) return def __add__(self, other): '''Return new Structure object with appended atoms from other. other -- sequence of Atom instances Return new Structure with a copy of Atom instances. ''' rv = copy.copy(self) rv += other return rv def __iadd__(self, other): '''Extend this Structure with atoms from other. other -- sequence of Atom instances Return self. ''' self.extend(other) return self def __sub__(self, other): '''Return new Structure that has atoms from the other removed. other -- sequence of Atom instances Return new Structure with a copy of Atom instances. ''' otherset = set(other) keepindices = [i for i, a in enumerate(self) if not a in otherset] rv = copy.copy(self[keepindices]) return rv def __isub__(self, other): '''Remove other atoms if present in this structure. other -- sequence of Atom instances Return self. ''' otherset = set(other) self[:] = [a for a in self if a not in otherset] return self def __mul__(self, n): '''Return new Structure with n-times concatenated atoms from self. Atoms and lattice in the new structure are all copies. n -- integer multiple Return new Structure. ''' rv = copy.copy(self[:0]) rv += n * self.tolist() return rv # right-side multiplication is the same as left-side __rmul__ = __mul__ def __imul__(self, n): '''Concatenate this Structure to n-times more atoms. For positive multiple the current Atom objects remain at the beginning of this Structure. n -- integer multiple Return self. ''' if n <= 0: self[:] = [] else: self.extend((n - 1) * self.tolist()) return self # Properties ------------------------------------------------------------- # lattice def _get_lattice(self): return self._lattice def _set_lattice(self, value): for a in self: a.lattice = value self._lattice = value return lattice = property(_get_lattice, _set_lattice, doc="Coordinate system for this Structure.") # composition def _get_composition(self): rv = {} for a in self: rv[a.element] = rv.get(a.element, 0.0) + a.occupancy return rv composition = property( _get_composition, doc="Dictionary of chemical symbols and their total occupancies.") # linked atom attributes element = _linkAtomAttribute( 'element', '''Character array of atom types. Assignment updates the element attribute of the respective atoms.''', toarray=numpy.char.array) xyz = _linkAtomAttribute( 'xyz', '''Array of fractional coordinates of all atoms. Assignment updates xyz attribute of all atoms.''') x = _linkAtomAttribute( 'x', '''Array of all fractional coordinates x. Assignment updates xyz attribute of all atoms.''') y = _linkAtomAttribute( 'y', '''Array of all fractional coordinates y. Assignment updates xyz attribute of all atoms.''') z = _linkAtomAttribute( 'z', '''Array of all fractional coordinates z. Assignment updates xyz attribute of all atoms.''') label = _linkAtomAttribute( 'label', '''Character array of atom names. Assignment updates the label attribute of all atoms.''', toarray=numpy.char.array) occupancy = _linkAtomAttribute( 'occupancy', '''Array of atom occupancies. Assignment updates the occupancy attribute of all atoms.''') xyz_cartn = _linkAtomAttribute( 'xyz_cartn', '''Array of absolute Cartesian coordinates of all atoms. Assignment updates the xyz attribute of all atoms.''') anisotropy = _linkAtomAttribute( 'anisotropy', '''Boolean array for anisotropic thermal displacement flags. Assignment updates the anisotropy attribute of all atoms.''') U = _linkAtomAttribute( 'U', '''Array of anisotropic thermal displacement tensors. Assignment updates the U and anisotropy attributes of all atoms.''') Uisoequiv = _linkAtomAttribute( 'Uisoequiv', '''Array of isotropic thermal displacement or equivalent values. Assignment updates the U attribute of all atoms.''') U11 = _linkAtomAttribute( 'U11', '''Array of U11 elements of the anisotropic displacement tensors. Assignment updates the U and anisotropy attributes of all atoms.''') U22 = _linkAtomAttribute( 'U22', '''Array of U22 elements of the anisotropic displacement tensors. Assignment updates the U and anisotropy attributes of all atoms.''') U33 = _linkAtomAttribute( 'U33', '''Array of U33 elements of the anisotropic displacement tensors. Assignment updates the U and anisotropy attributes of all atoms.''') U12 = _linkAtomAttribute( 'U12', '''Array of U12 elements of the anisotropic displacement tensors. Assignment updates the U and anisotropy attributes of all atoms.''') U13 = _linkAtomAttribute( 'U13', '''Array of U13 elements of the anisotropic displacement tensors. Assignment updates the U and anisotropy attributes of all atoms.''') U23 = _linkAtomAttribute( 'U23', '''Array of U23 elements of the anisotropic displacement tensors. Assignment updates the U and anisotropy attributes of all atoms.''') Bisoequiv = _linkAtomAttribute( 'Bisoequiv', '''Array of Debye-Waller isotropic thermal displacement or equivalent values. Assignment updates the U attribute of all atoms.''') B11 = _linkAtomAttribute( 'B11', '''Array of B11 elements of the Debye-Waller displacement tensors. Assignment updates the U and anisotropy attributes of all atoms.''') B22 = _linkAtomAttribute( 'B22', '''Array of B22 elements of the Debye-Waller displacement tensors. Assignment updates the U and anisotropy attributes of all atoms.''') B33 = _linkAtomAttribute( 'B33', '''Array of B33 elements of the Debye-Waller displacement tensors. Assignment updates the U and anisotropy attributes of all atoms.''') B12 = _linkAtomAttribute( 'B12', '''Array of B12 elements of the Debye-Waller displacement tensors. Assignment updates the U and anisotropy attributes of all atoms.''') B13 = _linkAtomAttribute( 'B13', '''Array of B13 elements of the Debye-Waller displacement tensors. Assignment updates the U and anisotropy attributes of all atoms.''') B23 = _linkAtomAttribute( 'B23', '''Array of B23 elements of the Debye-Waller displacement tensors. Assignment updates the U and anisotropy attributes of all atoms.''') # Private Methods -------------------------------------------------------- def __emptySharedStructure(self): '''Return empty Structure with standard attributes same as in self. ''' rv = Structure() rv.__dict__.update([(k, getattr(self, k)) for k in rv.__dict__]) return rv
class QELattice(object): """Class QELattice for working with crystal lattices in QE notation Uses diffpy.Lattice class for storage Following parameters are dynamically linked to other properties (E.g. lattice vectors): ibrav - lattice type if ibrav = 0, only 'a' prameter is relevant a, b, c, cBC, cAC ,cAB - lattice parameters type - lattice type to save into PWSCF cfg file ('celldm'); 'traditional' - using a,b,c,cosAC, cosAB, cosBC; 'generic cubic', 'generic hexagonal' - assume exiasting section 'CELL_PARAMETERS', 'generic' types also assume/set ibrav = 0 setLattice() will set everything at once """ def __init__(self, ibrav = 1,a = 1. ,b = 1.,c = 1., cBC = 0.,cAC = 0. ,cAB = 0., fname = None, base = None ): # Lattice.__init__(self) self.filename = fname self._type = 'celldm' self.qeConf = None # should be none if nothing to parse self._primitiveLattice = Lattice() self._standardLattice = Lattice() self._base = None self._a0 = None if self.filename != None: self.setLatticeFromPWSCF(self.filename) else: if ibrav > 0 and base != None: self.setLatticeFromQEVectors(ibrav, base) else: self.setLattice(ibrav ,a ,b , c, cBC ,cAC ,cAB, base) def setLatticeFromQEVectors(self, ibrav, vectors): """ Will extract conventional lattice parameters from primitive vectors. 'vectors' - is a list with primitive vectors (in QE notation), including lattice parameters. For example from PWSCF output""" from numpy import dot # default values: a = b = c = 1.0 cBC = cAC = cAB = 0.0 v = numpy.array(vectors, dtype = float) if ibrav == 0: raise NotImplementedError # sc simple cubic: if ibrav == 1: a = v[0,0] if ibrav == 2: a = b = c = sqrt( 2.0*dot(v[0,:],v[0,:]) ) if ibrav == 3: a = b = c = 2.0*sqrt( dot(v[0,:],v[0,:])/3.0) if ibrav == 4: a = b = sqrt( dot(v[0,:],v[0,:])) c = sqrt( dot(v[2,:],v[2,:])) cAB = cosd(120.0) if ibrav == 5: a = b = c = sqrt( dot(v[0,:],v[0,:])) cBC = cAC = cAB = dot(v[0,:],v[2,:])/a**2 if ibrav == 6: a = b = sqrt( dot(v[0,:],v[0,:])) c = sqrt( dot(v[2,:],v[2,:])) if ibrav == 7: a = b = v[1,0] - v[2,0] c = v[1,2] + v[2,2] if ibrav == 8: a = v[0,0] b = v[1,1] c = v[2,2] if ibrav == 9: a = v[0,0] - v[1,0] b = v[0,1] + v[1,1] c = v[2,2] if ibrav == 10: a = v[2,0] - v[0,0] - v[1,0] b = v[2,1] - v[0,1] + v[1,1] c = v[0,2] - v[1,2] + v[2,2] if ibrav == 11: a = v[0,0] - v[1,0] b = v[1,1] - v[2,1] c = v[0,2] - v[2,2] if ibrav == 12: a = v[0,0] b = sqrt( dot(v[1,:],v[1,:])) cAB = v[1,0]/b c = v[2,2] if ibrav == 13: a = v[0,0] + v[2,0] b = sqrt( dot(v[1,:],v[1,:])) c = v[2,2] - v[0,2] cAB = v[1,0]/b if ibrav == 14: a = v[0,0] b = sqrt( dot(v[1,:],v[1,:])) cAB = v[1,0]/b c = sqrt( dot(v[2,:],v[2,:])) cAC = v[2,0]/c cBC = v[2,1]*sqrt(1.0 - cAB**2)/c + cAC*cAB self.setLattice(ibrav, a, b, c, cBC, cAC, cAB) def setLatticeFromPWSCF(self, fname = None): if fname != None: self.filename = fname self.qeConf = QEConfig(fname) self.qeConf.parse() if 'ibrav' in self.qeConf.namelists['system'].params: ibrav = int(self.qeConf.namelist('system').param('ibrav')) if ibrav >= 0: a, b, c, cBC, cAC, cAB, base = self.getLatticeParamsFromPWSCF(ibrav, fname) else: raise NotImplementedError("ibrav should be integer >= 0") else: raise NotImplementedError("config file should have ibrav defined") self.setLattice(ibrav, a, b, c, cBC, cAC, cAB, base) def setLattice(self, ibrav, a = None, b = None, c = None, cBC = None, cAC = None, cAB = None, base = None): """ 'base', numpy array of lattice vectors, and 'a' will only be used if ibrav == 0. Otherwise, ibrav + lattice parameters will be used""" from math import acos # if [ibrav, a,b,c,cBC,cAC,cAB, base] == 8*[None]: # return None if ibrav == None: raise NonImplementedError('ibrav should be specified') self._ibrav = ibrav self._a0 = a if self._ibrav == 0: # print 'Found "generic" cell:' if base == None: raise NonImplementedError('base must be specified') if a == None: a = 1.0 qeBase = numpy.array(base, dtype = float)*a # print qeBase self._a = 1.0 self._primitiveLattice.setLatBase(qeBase) self._standardLattice.setLatBase(qeBase) else: # Make sure all lattice parameters are mutually consistent # according to ibrav. base array is not used: if ibrav < 4 or ibrav == 5: if a is not None: self._a = self._b = self._c = a else: if b is not None: self._a = self._b = self._c = b else: if c is not None: self._a = self._b = self._c = c else: self._b = self._c = self._a if ibrav < 4: self._cBC = self._cAC = self._cAB = 0.0 if ibrav == 4 or ibrav == 6 or ibrav == 7: if a is not None: self._a = self._b = a else: if b is not None: self._a = self._b = b else: self._b = self._a if c is not None: self._c = c if ibrav == 4: self._cAB = cosd(120.0) self._cBC = self._cAC = 0.0 if ibrav == 5: if cBC is not None: self._cBC = self._cAC = self._cAB = cBC else: if cAC is not None: self._cBC = self._cAC = self._cAB = cAC else: if cAB is not None: self._cBC = self._cAC = self._cAB = cAB else: self._cAC = self._cAB = self._cBC if ibrav == 6 or ibrav == 7: self._cBC = self._cAC = self._cAB = 0.0 if ibrav > 7 and ibrav <= 14: if a is not None: self._a = a if b is not None: self._b = b if c is not None: self._c = c if ibrav > 7 and ibrav < 12: self._cBC = self._cAC = self._cAB = 0.0 if ibrav == 12 or ibrav == 13: if cAB is not None: self._cAB = cAB else: if cBC is not None or cAC is not None: raise Exception("Should specify cos(AB) only for" + \ " ibrav = 12 or ibrav = 13" ) self._cBC = self._cAC = 0.0 if ibrav == 14: if cBC is not None: self._cBC = cBC if cAC is not None: self._cAC = cAC if cAB is not None: self._cAB = cAB # if a is not None: self._a = a # if b is not None: self._b = b # if c is not None: self._c = c # if cBC is not None: self._cBC = cBC # if cAC is not None: self._cAC = cAC # if cAB is not None: self._cAB = cAB qeBaseTuple = self._getQEBaseFromParCos(self._ibrav, self._a, self._b, self._c, self._cBC, self._cAC, self._cAB) qeBase = numpy.array(qeBaseTuple[1], dtype = float)*qeBaseTuple[0] # print 'Found "' + qeBaseTuple[2] + '" cell' # print 'Setting the base vectors according to QE conventions:' # print qeBase self._primitiveLattice.setLatBase(qeBase) alpha = degrees(acos(self._cBC)) beta = degrees(acos(self._cAC)) gamma = degrees(acos(self._cAB)) self._standardLattice.setLatPar(self._a,self._b,self._c,alpha,beta,gamma) # print "Standard Lattice:" # print self._standardLattice.base self._base = qeBase def printBase(self): if self._ibrav == 0: print '"generic" cell:' else: qeBaseTuple = self._getQEBaseFromParCos(self._ibrav, self._a, self._b, self._c, self._cBC, self._cAC, self._cAB) qeBase = numpy.array(qeBaseTuple[1], dtype = float)*qeBaseTuple[0] print '"' + qeBaseTuple[2] + '" cell:' print qeBase def latticeParams(self): return [self._a, self._b,self._c, self._cBC, self._cAC, self._cAB] def diffpy(self): '''Returns diffpy.Lattice object. Do not use it for reading QE (standard cell) lattice parameters. Use latticeParams, or a, b, c , ... instead''' return self._primitiveLattice def getLatticeParamsFromPWSCF(self, ibrav, fname): qeConf = QEConfig(fname) qeConf.parse() cBC = 0.0 cAC = 0.0 cAB = 0.0 if 'celldm(1)' in qeConf.namelists['system'].params: self._type = 'celldm' # celldm(i), i=1,6 a = float(qeConf.namelist('system').param('celldm(1)')) if ibrav == 0: # lattice is set in the units of celldm(1) # need to parse CELL_PARAMETERS cellParLines = qeConf.card('cell_parameters').getLines() cellParType = qeConf.card('cell_parameters').argument() if cellParType == 'cubic' or cellParType == None: self._type = 'generic cubic' else: if cellParType == 'hexagonal': self._type = 'generic hexagonal' # convert card into list base = [] for line in cellParLines: if '!' not in line: words = line.split() base.append([float(w) for w in words]) return a, None, None, None, None, None, numpy.array(base) if ibrav > 0 and ibrav < 4: return a, a, a, cBC, cAC, cAB, None if ibrav == 4: cAB = cosd(120.0) if ibrav == 4 or ibrav == 6 or ibrav == 7: c_a = float(qeConf.namelist('system').param('celldm(3)')) return a, a, c_a*a, cBC, cAC, cAB, None if ibrav == 5: cAB = float(qeConf.namelist('system').param('celldm(4)')) return a, a, a, cAB, cAB, cAB, None if ibrav > 7 and ibrav < 12: b_a = float(qeConf.namelist('system').param('celldm(2)')) c_a = float(qeConf.namelist('system').param('celldm(3)')) return a, b_a*a, c_a*a, cBC, cAC, cAB, None if ibrav == 12 or ibrav == 13: b_a = float(qeConf.namelist('system').param('celldm(2)')) c_a = float(qeConf.namelist('system').param('celldm(3)')) cAB = float(qeConf.namelist('system').param('celldm(4)')) return a, b_a*a, c_a*a, cBC, cAC, cAB, None if ibrav == 14: b_a = float(qeConf.namelist('system').param('celldm(2)')) c_a = float(qeConf.namelist('system').param('celldm(3)')) cBC = float(qeConf.namelist('system').param('celldm(4)')) cAC = float(qeConf.namelist('system').param('celldm(5)')) cAB = float(qeConf.namelist('system').param('celldm(6)')) return a, b_a*a, c_a*a, cBC, cAC, cAB, None else: if ibrav == 0: print "Should specify celldm(1) if use 'generic' lattice" raise NotImplementedError a = float(qeConf.namelist('system').param('A')) self._type = 'traditional' # A, B, C, cosAB, cosAC, cosBC if ibrav > 0 and ibrav < 4: return a, a, a, cBC, cAC, cAB, None if ibrav == 4: cAB = cosd(120.0) if ibrav == 4 or ibrav == 6 or ibrav == 7: c = float(qeConf.namelist('system').param('C')) return a, a, c, cBC, cAC, cAB, None if ibrav == 5: cAB = float(qeConf.namelist('system').param('cosAB')) return a, a, a, cAB, cAB, cAB, None if ibrav > 7 and ibrav < 12: b = float(qeConf.namelist('system').param('B')) c = float(qeConf.namelist('system').param('C')) return a, b, c, cBC, cAC, cAB, None if ibrav == 12 or ibrav == 13: b = float(qeConf.namelist('system').param('B')) c = float(qeConf.namelist('system').param('C')) cAB = float(qeConf.namelist('system').param('cosAB')) return a, b, c, cBC, cAC, cAB, None if ibrav == 14: b = float(qeConf.namelist('system').param('B')) c = float(qeConf.namelist('system').param('C')) cBC = float(qeConf.namelist('system').param('cosBC')) cAC = float(qeConf.namelist('system').param('cosAC')) cAB = float(qeConf.namelist('system').param('cosAB')) return a, b, c, cBC, cAC, cAB, None def saveLatticeToPWSCF(self, fname = None): """Will save the lattice either into its own file or into supplied with fname. It will also create all relevant sections/cards""" if fname != None: filename = fname qeConf = QEConfig(fname) qeConf.parse() else: if self.filename != None: filename = self.filename qeConf = self.qeConf else: raise # saveLatticeToPWSCF, filename was not supplied if qeConf == None: raise NotImplementedError("writeLatticeToPWSCF: qeConf was not properly initialized") if 'system' not in qeConf.namelists: qeConf.createNamelist('system') # clear geometry from qeConf: qeConf.namelist('system').removeParam('a') qeConf.namelist('system').removeParam('b') qeConf.namelist('system').removeParam('c') qeConf.namelist('system').removeParam('cosab') qeConf.namelist('system').removeParam('cosbc') qeConf.namelist('system').removeParam('cosac') qeConf.namelist('system').removeParam('celldm(1)') qeConf.namelist('system').removeParam('celldm(2)') qeConf.namelist('system').removeParam('celldm(3)') qeConf.namelist('system').removeParam('celldm(4)') qeConf.namelist('system').removeParam('celldm(5)') qeConf.namelist('system').removeParam('celldm(6)') if 'cell_parameters' in qeConf.cards: qeConf.removeCard('cell_parameters') if self._type == 'celldm': qeConf.namelist('system').addParam('ibrav', self._ibrav) qeConf.namelist('system').addParam('celldm(1)', self._a) qeConf.namelist('system').addParam('celldm(2)', self._b/self._a) qeConf.namelist('system').addParam('celldm(3)', self._c/self._a) if self._ibrav < 14: qeConf.namelist('system').addParam('celldm(4)', self._cAB) else: qeConf.namelist('system').addParam('celldm(4)', self._cBC) qeConf.namelist('system').addParam('celldm(5)', self._cAC) qeConf.namelist('system').addParam('celldm(6)', self._cAB) else: if self._type == 'traditional': qeConf.namelist('system').addParam('ibrav', self._ibrav) qeConf.namelist('system').addParam('A', self._a) qeConf.namelist('system').addParam('B', self._b) qeConf.namelist('system').addParam('C', self._c) qeConf.namelist('system').addParam('cosAB', self._cAB) qeConf.namelist('system').addParam('cosAC', self._cAC) qeConf.namelist('system').addParam('cosBC', self._cBC) else: if 'generic' in self._type: qeConf.namelist('system').addParam('celldm(1)', 1.0) self._ibrav = 0 qeConf.namelist('system').addParam('ibrav', self._ibrav) if self._type == 'generic hexagonal': cardArg = 'hexagonal' if self._type == 'generic cubic' or self._type == None: cardArg = 'cubic' qeConf.createCard('cell_parameters') qeConf.card('cell_parameters').setArgument(cardArg) qeConf.card('cell_parameters').removeLines() for i in range(3): v = self._primitiveLattice.base[i,:] qeConf.card('cell_parameters').addLine(str(v)[1:-1]) qeConf.save(filename) def recipCartesian(self, kPoint): """Conversts vector on fractional coordinates in reciprocal space into a vector in cartesian coordinates""" recip_base = self.diffpy().reciprocal().base*self._a return numpy.dot( kPoint, recip_base) def _getQEBaseFromParCos( self, ibrav = 1, a = 1., b = 1., c = 1., cBC = 0.,cAC = 0. ,cAB = 0.): c_a = float(c)/a # description dictionary of QE base vectors: QEBase = { # sc simple cubic: 1 : (a, [[1, 0, 0], [0, 1, 0], [0, 0, 1]], 'Simple Cubic'), # fcc face centered cubic: 2 : (a/2., [[-1, 0, 1], [0, 1, 1], [-1, 1, 0]], 'Face Centered Cubic'), # bcc body entered cubic: 3 : (a/2., [[1, 1, 1], [-1, 1, 1], [-1, -1, 1]], 'Body Centered Cubic'), # simple hexagonal and trigonal(p): 4 : (a, [[1, 0, 0], [-0.5, sqrt(3.0)/2.0, 0.], [0, 0, c_a]], 'Simple Hexagonal or Trigonal(P)'), # trigonal(r): 5 : (a, [[sqrt((1.-cAB)/2.),-sqrt((1.-cAB)/6.), sqrt((1.+2.*cAB)/3.)], [0, 2.*sqrt((1.-cAB)/6.), sqrt((1.+2.*cAB)/3.)], [-sqrt((1.-cAB)/2.), -sqrt((1.-cAB)/6.), sqrt((1.+2.*cAB)/3.)]], 'Trigonal(R)'), # simple tetragonal (p): 6 : (a, [[1, 0, 0], [0, 1, 0.], [0, 0, c_a]], 'Simple Tetragonal(P)'), # body centered tetragonal (i): 7 : (a/2., [[1, -1, c_a], [1, 1, c_a], [-1, -1, c_a]], 'Body Centered Tetragonal (I)'), # simple orthorhombic (p): 8 : (1.0, [[a, 0., 0.], [0., b, 0.], [0., 0., c]], 'Simple Orthorhombic (P)'), # bco base centered orthorhombic: 9: (1.0, [[a/2., b/2., 0.], [-a/2., b/2., 0.], [0., 0., c]], 'Base Centered Orthorhombic'), # face centered orthorhombic: 10: (1.0, [[a/2., 0., c/2.], [a/2., b/2., 0.], [0., b/2., c/2.]], 'Face Centered Orthorhombic' ), # body centered orthorhombic: 11: (1.0, [[a/2., b/2., c/2.], [-a/2., b/2., c/2.], [-a/2., -b/2., c/2.]], 'Body Centered Orthorhombic'), # monoclinic (p): 12: (1.0, [[a, 0, 0], [b*cAB, b*sqrt(1.0 - cAB**2), 0], [0, 0, c]], 'Monoclinic (P)'), # base centered monoclinic: 13: (1.0, [[a/2., 0, -c/2.], [b*cAB, b*sqrt(1.0 - cAB**2), 0], [a/2., 0, c/2.]], 'Base Centered Monoclinic'), # triclinic: 14: (1.0, [[a, 0, 0], [b*cAB, b*sqrt(1.0 - cAB**2), 0], [c*cAC, c*( cBC-cAC*cAB )/sqrt(1.-cAB**2), c*sqrt( 1. + 2.*cBC*cAC*cAB - cBC**2 - cAC**2 - cAB**2)/sqrt(1.-cAB**2)]], 'Triclinic') } return QEBase[ibrav] #################################################################### # property handlers #################################################################### # lattice parameters def _get_a0(self): if self._a0 != None: return self._a0 else: return self._a a0 = property(_get_a0, doc ="old lattice parameter a0") def _get_a(self): return self._a def _set_a(self, value): self._a = value self.setLattice(ibrav = self._ibrav, a = self._a) a = property(_get_a, _set_a, doc ="lattice parameter a") def _get_b(self): return self._b def _set_b(self, value): self._b = value self.setLattice(ibrav = self._ibrav, b = self._b) b = property(_get_b, _set_b, doc ="""lattice parameter b""") def _get_c(self): return self._c def _set_c(self, value): self._c = value self.setLattice(ibrav = self._ibrav, c = self._c) c = property(_get_c, _set_c, doc ="""lattice parameter c""") def _get_cBC(self): return self._cBC def _set_cBC(self, value): self._cBC = value self.setLattice(ibrav = self._ibrav, cBC = self._cBC) cBC = property(_get_cBC, _set_cBC, doc ="""lattice parameter cBC""") def _get_cAC(self): return self._cAC def _set_cAC(self, value): self._cAC = value self.setLattice(ibrav = self._ibrav, cAC = self._cAC) cAC = property(_get_cAC, _set_cAC, doc ="""lattice parameter cAC""") def _get_cAB(self): return self._cAB def _set_cAB(self, value): self._cAB = value self.setLattice(ibrav = self._ibrav, cAB = self._cAB) cAB = property(_get_cAB, _set_cAB, doc ="""lattice parameter cAB""") def _get_ibrav(self): return self._ibrav def _set_ibrav(self, value): if value < 0: value = 0 ibravOld = self._ibrav self._ibrav = value if value == 0: base = self._base/self._a if ibravOld != 4: self._type = 'generic cubic' else: self._type = 'generic hexagonal' self.setLattice(ibrav = self._ibrav, a = self._a, base = base) else: if 'generic' in self._type: self._type = 'celldm' self.setLatticeFromQEVectors(self._ibrav, self.diffpy().base) # self.setLattice(self._ibrav) ibrav = property(_get_ibrav, _set_ibrav, doc ="""Lattice symmetry parameter ibrav""") def _get_type(self): return self._type def _set_type(self, value): if 'generic' in value: self._type = value self._ibrav = 0 base = self._base/self._a self.setLattice(ibrav = self._ibrav, a = self._a, base = base) else: if self._ibrav == 0: pass else: self._type = value type = property(_get_type, _set_type, doc ="""QE lattice type: 'celldm', 'traditional' or 'generic cubic', 'generic hexagonal'(implies ibrav = 0""")
def setReducedStructureFromDiffpyStructure(self, structure, ibrav, massList = [], psList = []): """ structure - diffpy.Structure object ibrav - Lattice index psList - list of strings with pseudopotential names diffpyStructure object will be modified with reduced atomic positions """ #self.atomicSpecies = OrderedDict() #self.optConstraints = [] #self.atomicPositionsType = 'crystal' diffpyLattice = structure.lattice a = diffpyLattice.a b = diffpyLattice.b c = diffpyLattice.c cAB = cosd(diffpyLattice.gamma) cBC = cosd(diffpyLattice.alpha) cAC = cosd(diffpyLattice.beta) qeLattice = QELattice(ibrav = ibrav, a = a, b = b, c = c, cBC = cBC, \ cAC = cAC, cAB = cAB) self.lattice = qeLattice # make a deep copy (does not wok now) #reducedStructure = Structure(diffpyStructure) reducedStructure = structure reducedStructure.placeInLattice(Lattice(base=qeLattice.diffpy().base)) # collect atoms that are at equivalent position to some previous atom duplicates = set([a1 for i0, a0 in enumerate(reducedStructure) for a1 in reducedStructure[i0+1:] if self._element(a0) == self._element(a1) and equalPositions(a0.xyz, a1.xyz, eps=1e-4)]) # Filter out duplicate atoms. Use slice assignment so that # reducedStructure is not replaced with a list. reducedStructure[:] = [a for a in reducedStructure if not a in duplicates] self.structure = reducedStructure atomNames = [] for a in reducedStructure: if self._element(a) not in atomNames: atomNames.append(self._element(a)) #print atomNames #print len(massList) for i, elem in enumerate(atomNames): if len(massList) - 1 < i: mass = 0 else: mass = massList[i] if len(psList) - 1 < i: ps = '' else: ps = psList[i] #print mass, ps # atomDict[a] = # for i, atom in enumerate(reducedStructure): # elem = self._element(atom) # if len(massList) - 1 < i: # mass = 0 # else: # mass = massList[i] # if len(psList) - 1 < i: # ps = '' # else: # ps = psList[i] self.atomicSpecies[elem] = AtomicSpecies(elem, mass, ps) for atom in reducedStructure: self.optConstraints.append([]) # convert to bohr units self.lattice.setLattice(ibrav, self.lattice.a*1.889725989, \ self.lattice.b*1.889725989, self.lattice.c*1.889725989) self.nat = len(reducedStructure) self.ntyp = len(self.atomicSpecies)
class Structure(list): """Structure --> group of atoms Structure class is inherited from Python list. It contains a list of Atom instances. Structure overloads setitem and setslice methods so that the lattice attribute of atoms get set to lattice. Data members: title -- structure description lattice -- coordinate system (instance of Lattice) pdffit -- None or a dictionary of PDFFit-related metadata """ # default values for instance attributes title = '' _lattice = None pdffit = None def __init__(self, atoms=[], lattice=None, title=None, filename=None, format=None): """define group of atoms in a specified lattice. atoms -- list of Atom instances to be included in this Structure. When atoms argument is an existing Structure instance, the new Structure is its copy. lattice -- instance of Lattice defining coordinate systems, property. title -- string description of the structure filename -- optional, name of a file to load the structure from. Overrides atoms argument when specified. format -- optional structure format of the loaded filename. By default all structure formats are tried one by one. Ignored when filename has not been specified. Structure(stru) create a copy of Structure instance stru. Because Structure is inherited from a list it can use list expansions, for example: oxygen_atoms = [ for a in stru if a.element == "O" ] oxygen_stru = Structure(oxygen_atoms, lattice=stru.lattice) """ # if filename is specified load it and return if filename is not None: if any((atoms, lattice, title)): emsg = "Cannot use filename and atoms arguments together." raise ValueError(emsg) readkwargs = (format is not None) and {'format' : format} or {} self.read(filename, **readkwargs) return # copy initialization, must be first to allow lattice, title override if isinstance(atoms, Structure): Structure.__copy__(atoms, self) # assign arguments: if title is not None: self.title = title if lattice is not None: self.lattice = lattice elif self.lattice is None: self.lattice = Lattice() # insert atoms unless already done by __copy__ if not len(self) and len(atoms): self.extend(atoms) return def __copy__(self, target=None): '''Create a deep copy of this instance. target -- optional target instance for copying, useful for copying a derived class. Defaults to new instance of the same type as self. Return a duplicate instance of this object. ''' if target is None: target = Structure() elif target is self: return target # copy attributes as appropriate: target.title = self.title target.lattice = Lattice(self.lattice) target.pdffit = copy.deepcopy(self.pdffit) # copy all atoms to the target target[:] = self return target def __str__(self): """simple string representation""" s_lattice = "lattice=%s" % self.lattice s_atoms = '\n'.join([str(a) for a in self]) return s_lattice + '\n' + s_atoms 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 = Atom(*args, **kwargs) self.append(a, copy=False) return def getLastAtom(self): """Return Reference to the last Atom in this structure. """ last_atom = self[-1] return last_atom def assignUniqueLabels(self): """Set a unique label string for each atom in this structure. The label strings are formatted as "%(baresymbol)s%(index)i", where baresymbol is the element right-stripped of "[0-9][+-]". No return value. """ elnum = {} # support duplicate atom instances islabeled = set() for a in self: if a in islabeled: continue baresmbl = atomBareSymbol(a.element) elnum[baresmbl] = elnum.get(baresmbl, 0) + 1 a.label = baresmbl + str(elnum[baresmbl]) islabeled.add(a) return def distance(self, aid0, aid1): """Distance between 2 atoms, no periodic boundary conditions. aid0 -- zero based index of the first atom or a string label such as "Na1" aid1 -- zero based index or string label of the second atom. Return float. Raise IndexError for invalid arguments. """ # lookup by labels a0, a1 = self[aid0, aid1] return self.lattice.dist(a0.xyz, a1.xyz) def angle(self, aid0, aid1, aid2): """The bond angle at the second of three atoms in degrees. aid0 -- zero based index of the first atom or a string label such as "Na1" aid1 -- index or string label for the second atom, where the angle is formed aid2 -- index or string label for the third atom Return float. Raise IndexError for invalid arguments. """ a0, a1, a2 = self[aid0, aid1, aid2] u10 = a0.xyz - a1.xyz u12 = a2.xyz - a1.xyz return self.lattice.angle(u10, u12) 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 positions remain the same return self """ Tx = numpy.dot(self.lattice.base, new_lattice.recbase) Tu = numpy.dot(self.lattice.normbase, new_lattice.recnormbase) for a in self: a.xyz = numpy.dot(a.xyz, Tx) if a.anisotropy: a.U = numpy.dot(numpy.transpose(Tu), numpy.dot(a.U, Tu)) self.lattice = new_lattice return self def read(self, filename, format='auto'): """Load structure from a file, any original data become lost. filename -- file to be loaded format -- all structure formats are defined in Parsers submodule, when format == 'auto' all Parsers are tried one by one Return instance of data Parser used to process file. This can be inspected for information related to particular format. """ import diffpy.Structure import diffpy.Structure.Parsers getParser = diffpy.Structure.Parsers.getParser p = getParser(format) new_structure = p.parseFile(filename) # reinitialize data after successful parsing # avoid calling __init__ from a derived class Structure.__init__(self) if new_structure is not None: self.__dict__.update(new_structure.__dict__) self[:] = new_structure if not self.title: import os.path tailname = os.path.basename(filename) tailbase = os.path.splitext(tailname)[0] self.title = tailbase return p def readStr(self, s, format='auto'): """Load structure from a string, any original data become lost. s -- string with structure definition format -- all structure formats are defined in Parsers submodule, when format == 'auto' all Parsers are tried one by one Return instance of data Parser used to process input string. This can be inspected for information related to particular format. """ from diffpy.Structure.Parsers import getParser p = getParser(format) new_structure = p.parse(s) # reinitialize data after successful parsing # avoid calling __init__ from a derived class Structure.__init__(self) if new_structure is not None: self.__dict__.update(new_structure.__dict__) self[:] = new_structure return p def write(self, filename, format): """Save structure to file in the specified format No return value. Note: available structure formats can be obtained by: from Parsers import formats """ from diffpy.Structure.Parsers import getParser p = getParser(format) p.filename = filename s = p.tostring(self) f = open(filename, 'wb') f.write(s) f.close() return def writeStr(self, format): """return string representation of the structure in specified format Note: available structure formats can be obtained by: from Parsers import formats """ from diffpy.Structure.Parsers import getParser p = getParser(format) s = p.tostring(self) return s def tolist(self): '''Return atoms in this Structure as a standard Python list. ''' rv = [a for a in self] return rv # Overloaded list Methods and Operators ---------------------------------- def append(self, a, copy=True): """Append atom to a structure and update its lattice attribute. a -- instance of Atom copy -- flag for appending a copy of a. When False, append a and update a.lattice. No return value. """ adup = copy and Atom(a) or a adup.lattice = self.lattice list.append(self, adup) 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 Atom copy -- flag for inserting a copy of a. When False, append a and update a.lattice. No return value. """ adup = copy and Atom(a) or a adup.lattice = self.lattice list.insert(self, idx, adup) return def extend(self, atoms, copy=True): """Extend Structure by appending copies from a list of atoms. atoms -- list of Atom instances copy -- flag for extending with copies of Atom instances. When False extend with atoms and update their lattice attributes. No return value. """ if copy: adups = [Atom(a) for a in atoms] else: adups = atoms for a in adups: a.lattice = self.lattice list.extend(self, adups) return def __getitem__(self, idx): """Get one or more atoms in this structure. idx -- atom identifier, which can be integer, string or iterable. When integer use standard list lookup. For iterables use numpy lookup, this supports integer or boolean flag arrays. For string or string-containing iterables lookup the atoms by string label. Return an Atom instance for integer or string index or a substructure in all other cases. Raise IndexError for invalid index or for non-unique atom label. Examples: stru[0] --> first atom in the Structure stru[stru.element == 'Na'] --> substructure of all Na atoms stru['Na3'] --> atom with a unique label 'Na3' stru['Na3', 2, 'Cl2'] --> substructure of three atoms, lookup by label is more efficient when done for several atoms at once. """ try: value = list.__getitem__(self, idx) rv = value if type(idx) is slice: rv = self.__emptySharedStructure() rv.extend(value, copy=False) return rv except TypeError: pass # check if there is any string label that should be resolved scalarlabel = type(idx) is str hasstringlabel = scalarlabel or str in map(type, idx) # if not, use numpy indexing to resolve idx if not hasstringlabel: idx1 = idx if type(idx) is tuple: idx1 = numpy.r_[idx] indices = numpy.arange(len(self))[idx1] rhs = [list.__getitem__(self, i) for i in indices] rv = self.__emptySharedStructure() rv.extend(rhs, copy=False) return rv # here we need to resolve at least one string label # build a map of labels to indices and mark duplicate labels duplicate = () labeltoindex = {} for i, a in enumerate(self): labeltoindex[a.label] = ( a.label in labeltoindex and duplicate or i) def _resolveindex(aid): aid1 = aid if type(aid) is str: aid1 = labeltoindex.get(aid, None) if aid1 is None: raise IndexError("Invalid atom label %r." % aid) if aid1 is duplicate: raise IndexError("Atom label %r is not unique." % aid) return aid1 # generate new index object that has no strings if scalarlabel: idx2 = _resolveindex(idx) # for iterables preserved the tuple object type else: idx2 = map(_resolveindex, idx) if type(idx) is tuple: idx2 = tuple(idx2) # call this function again and hope there is no recursion loop rv = self[idx2] return rv def __setitem__(self, idx, a, copy=True): """Set idx-th atom to a. idx -- index of atom in this Structure a -- instance of Atom copy -- flag for setting to a copy of a. When False, set to a and update a.lattice. No return value. """ adup = copy and Atom(a) or a adup.lattice = self.lattice list.__setitem__(self, idx, adup) return def __getslice__(self, lo, hi): '''Get a slice of atoms from this Structure. lo, hi -- slice indices, negative values are not supported Return a sub-structure with atom instances in the slice. ''' rv = self.__emptySharedStructure() rv.extend(list.__getslice__(self, lo, hi), copy=False) return rv 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. """ if copy: ownatoms = set(list.__getslice__(self, lo, hi)) adups = [(a in ownatoms and a or Atom(a)) for a in atoms] else: adups = atoms for a in adups: a.lattice = self.lattice list.__setslice__(self, lo, hi, adups) return def __add__(self, other): '''Return new Structure object with appended atoms from other. other -- sequence of Atom instances Return new Structure with a copy of Atom instances. ''' rv = copy.copy(self) rv += other return rv def __iadd__(self, other): '''Extend this Structure with atoms from other. other -- sequence of Atom instances Return self. ''' self.extend(other) return self def __sub__(self, other): '''Return new Structure that has atoms from the other removed. other -- sequence of Atom instances Return new Structure with a copy of Atom instances. ''' otherset = set(other) keepindices = [i for i, a in enumerate(self) if not a in otherset] rv = copy.copy(self[keepindices]) return rv def __isub__(self, other): '''Remove other atoms if present in this structure. other -- sequence of Atom instances Return self. ''' otherset = set(other) self[:] = [a for a in self if a not in otherset] return self def __mul__(self, n): '''Return new Structure with n-times concatenated atoms from self. Atoms and lattice in the new structure are all copies. n -- integer multiple Return new Structure. ''' rv = copy.copy(self[:0]) rv += n * self.tolist() return rv # right-side multiplication is the same as left-side __rmul__ = __mul__ def __imul__(self, n): '''Concatenate this Structure to n-times more atoms. For positive multiple the current Atom objects remain at the beginning of this Structure. n -- integer multiple Return self. ''' if n <= 0: self[:] = [] else: self.extend((n - 1) * self.tolist()) return self # Properties ------------------------------------------------------------- # lattice def _get_lattice(self): return self._lattice def _set_lattice(self, value): for a in self: a.lattice = value self._lattice = value return lattice = property(_get_lattice, _set_lattice, doc = "Coordinate system for this Structure.") # composition def _get_composition(self): rv = {} for a in self: rv[a.element] = rv.get(a.element, 0.0) + a.occupancy return rv composition = property(_get_composition, doc="Dictionary of chemical symbols and their total occupancies.") # linked atom attributes element = _linkAtomAttribute('element', '''Character array of atom types. Assignment updates the element attribute of the respective atoms.''', toarray=numpy.char.array) xyz = _linkAtomAttribute('xyz', '''Array of fractional coordinates of all atoms. Assignment updates xyz attribute of all atoms.''') x = _linkAtomAttribute('x', '''Array of all fractional coordinates x. Assignment updates xyz attribute of all atoms.''') y = _linkAtomAttribute('y', '''Array of all fractional coordinates y. Assignment updates xyz attribute of all atoms.''') z = _linkAtomAttribute('z', '''Array of all fractional coordinates z. Assignment updates xyz attribute of all atoms.''') label = _linkAtomAttribute('label', '''Character array of atom names. Assignment updates the label attribute of all atoms.''', toarray=numpy.char.array) occupancy = _linkAtomAttribute('occupancy', '''Array of atom occupancies. Assignment updates the occupancy attribute of all atoms.''') xyz_cartn = _linkAtomAttribute('xyz_cartn', '''Array of absolute Cartesian coordinates of all atoms. Assignment updates the xyz attribute of all atoms.''') anisotropy = _linkAtomAttribute('anisotropy', '''Boolean array for anisotropic thermal displacement flags. Assignment updates the anisotropy attribute of all atoms.''') U = _linkAtomAttribute('U', '''Array of anisotropic thermal displacement tensors. Assignment updates the U and anisotropy attributes of all atoms.''') Uisoequiv = _linkAtomAttribute('Uisoequiv', '''Array of isotropic thermal displacement or equivalent values. Assignment updates the U attribute of all atoms.''') U11 = _linkAtomAttribute('U11', '''Array of U11 elements of the anisotropic displacement tensors. Assignment updates the U and anisotropy attributes of all atoms.''') U22 = _linkAtomAttribute('U22', '''Array of U22 elements of the anisotropic displacement tensors. Assignment updates the U and anisotropy attributes of all atoms.''') U33 = _linkAtomAttribute('U33', '''Array of U33 elements of the anisotropic displacement tensors. Assignment updates the U and anisotropy attributes of all atoms.''') U12 = _linkAtomAttribute('U12', '''Array of U12 elements of the anisotropic displacement tensors. Assignment updates the U and anisotropy attributes of all atoms.''') U13 = _linkAtomAttribute('U13', '''Array of U13 elements of the anisotropic displacement tensors. Assignment updates the U and anisotropy attributes of all atoms.''') U23 = _linkAtomAttribute('U23', '''Array of U23 elements of the anisotropic displacement tensors. Assignment updates the U and anisotropy attributes of all atoms.''') Bisoequiv = _linkAtomAttribute('Bisoequiv', '''Array of Debye-Waller isotropic thermal displacement or equivalent values. Assignment updates the U attribute of all atoms.''') B11 = _linkAtomAttribute('B11', '''Array of B11 elements of the Debye-Waller displacement tensors. Assignment updates the U and anisotropy attributes of all atoms.''') B22 = _linkAtomAttribute('B22', '''Array of B22 elements of the Debye-Waller displacement tensors. Assignment updates the U and anisotropy attributes of all atoms.''') B33 = _linkAtomAttribute('B33', '''Array of B33 elements of the Debye-Waller displacement tensors. Assignment updates the U and anisotropy attributes of all atoms.''') B12 = _linkAtomAttribute('B12', '''Array of B12 elements of the Debye-Waller displacement tensors. Assignment updates the U and anisotropy attributes of all atoms.''') B13 = _linkAtomAttribute('B13', '''Array of B13 elements of the Debye-Waller displacement tensors. Assignment updates the U and anisotropy attributes of all atoms.''') B23 = _linkAtomAttribute('B23', '''Array of B23 elements of the Debye-Waller displacement tensors. Assignment updates the U and anisotropy attributes of all atoms.''') # Private Methods -------------------------------------------------------- def __emptySharedStructure(self): '''Return empty Structure with standard attributes same as in self. ''' rv = Structure() rv.__dict__.update([(k, getattr(self, k)) for k in rv.__dict__]) return rv
class QELattice(object): """Class QELattice for working with crystal lattices in QE notation Uses diffpy.Lattice class for storage Following properties are dynamically linked to other properties (E.g. lattice vectors) and QEInput(if QEInput.autoUpdate = True(default)): ibrav -- lattice type, setting it into a different value will automatically update lattice vectors, QE parsing object and structure if ibrav = 0, only 'a' parameter is relevant a, b, c, cBC, cAC ,cAB -- lattice parameters, setting any of them will dynamically update the QE parsing object (e.g. pw.input) and structure(if relevant). They are also ibrav sensitive. E.g. if ibrav = 1 (simple cubic). Setting 'a' to a \ different value will also modify b and c. type -- lattice type to save into PWSCF cfg file 'celldm' (default) 'traditional' - using a,b,c,cosAC, cosAB, cosBC; 'generic cubic', 'generic hexagonal' - assume that section 'CELL_PARAMETERS' exists 'generic' types also assume/set ibrav = 0 setLattice() -- will set everything at once """ def __init__(self, ibrav = 1,a = 1. ,b = 1.,c = 1., cBC = 0.,cAC = 0. ,cAB = 0., base = None, lattice = None ): self.formatString = '%# .8f %# .8f %# .8f' self._qeInput = None #self._qeInput = None self._type = 'celldm' # diffpyStructure container class, used for lattice operations self.__primitiveLattice = Lattice() # Lattice vectors in bohr or angstrom: self._base = None # initialize the lattice if there is enough information if ibrav > 0 and base != None: self.setLatticeFromQEVectors(ibrav, base) else: self.setLattice(ibrav ,a ,b , c, cBC ,cAC ,cAB, base) # copy constructor: if isinstance(ibrav, QELattice) or lattice != None: if lattice.ibrav > 0: self.setLattice( ibrav = lattice.ibrav, a = lattice.a, \ b = lattice.b, c = lattice.c, cBC = lattice.cBC, cAC = lattice.cAC,\ cAB = lattice.cAB) else: self.setLattice( ibrav = lattice.ibrav, a = lattice.a, \ base = lattice.base ) # copy input: from pwinput import PWInput self._qeInput = PWInput() self._qeInput.readString( lattice._qeInput.toString() ) def __str__(self): """simple string representation""" st = '' if self._ibrav == 0: st = st + '"generic" cell:\n' else: qeBaseTuple = self._getQEBaseFromParCos(self._ibrav, self._a, self._b, \ self._c, self._cBC, self._cAC, self._cAB) qeBase = numpy.array(qeBaseTuple[1], dtype = float)*qeBaseTuple[0] st = st + '"' + qeBaseTuple[2] + '" cell:\n' for i in range(3): v = self.__primitiveLattice.base[i,:] st = st + self.formatString%(v[0], v[1], v[2]) st = st + '\n' return st def cartesian(self, u): """return cartesian coordinates of a lattice vector""" return self.__primitiveLattice.cartesian(u) def fractional(self, rc): """return fractional coordinates of a cartesian vector""" return self.__primitiveLattice.fractional(u) def dot(self, u, v): """return dot product of 2 lattice vectors""" return self.__primitiveLattice.dot(u, v) def norm(self, u): """return norm of a lattice vector""" return self.__primitiveLattice.norm(u) def dist(self, u, v): """Return distance of 2 points in lattice coordinates. """ return __primitiveLattice.dist(u, v) def angle(self, u, v): """Return angle(u, v) --> angle of 2 lattice vectors in degrees. """ return __primitiveLattice.angle(u, v) def recipCartesian(self, kPoint): """Conversts a vector in fractional coordinates in reciprocal space into a vector in cartesian coordinates""" recip_base = self.diffpy().reciprocal().base*self._a return numpy.dot( kPoint, recip_base) def reciprocalBase(self): """ Get reciprocal lattice vectors in units of 2*pi/a """ return self.diffpy().reciprocal().base*self._a def latticeParams(self): """Returns tuple of six lattice parameters: a, b, c, cos(BC), cos(AC), cos(AB) """ return [self._a, self._b,self._c, self._cBC, self._cAC, self._cAB] def setLattice(self, ibrav, a = None, b = None, c = None, cBC = None, cAC = None, cAB = None, base = None, updateInput = True): """ 'base', numpy array of lattice vectors, and 'a' will only be used if ibrav == 0. Otherwise, ibrav + lattice parameters will be used""" from math import acos # if [ibrav, a,b,c,cBC,cAC,cAB, base] == 8*[None]: # return None if ibrav == None: raise NonImplementedError('ibrav should be specified') self._ibrav = ibrav if self._ibrav == 0: # print 'Found "generic" cell:' if base == None: raise NonImplementedError('base must be specified') if a == None: raise # a and the base must be provided for ibrav = 0 qeBase = numpy.array(base, dtype = float)#*self._a/a self._a = a # print qeBase #self._a = 1.0 if 'generic' not in self._type: self._type = 'generic cubic' self.__primitiveLattice.setLatBase(qeBase) #self._standardLattice.setLatBase(qeBase) else: # Make sure all lattice parameters are mutually consistent # according to ibrav. base array is not used: if ibrav < 4 or ibrav == 5: if a is not None: self._a = self._b = self._c = a else: if b is not None: self._a = self._b = self._c = b else: if c is not None: self._a = self._b = self._c = c else: self._b = self._c = self._a if ibrav < 4: self._cBC = self._cAC = self._cAB = 0.0 if ibrav == 4 or ibrav == 6 or ibrav == 7: if a is not None: self._a = self._b = a else: if b is not None: self._a = self._b = b else: self._b = self._a if c is not None: self._c = c if ibrav == 4: self._cAB = cosd(120.0) self._cBC = self._cAC = 0.0 if ibrav == 5: if cBC is not None: self._cBC = self._cAC = self._cAB = cBC else: if cAC is not None: self._cBC = self._cAC = self._cAB = cAC else: if cAB is not None: self._cBC = self._cAC = self._cAB = cAB else: self._cAC = self._cAB = self._cBC if ibrav == 6 or ibrav == 7: self._cBC = self._cAC = self._cAB = 0.0 if ibrav > 7 and ibrav <= 14: if a is not None: self._a = a if b is not None: self._b = b if c is not None: self._c = c if ibrav > 7 and ibrav < 12: self._cBC = self._cAC = self._cAB = 0.0 if ibrav == 12 or ibrav == 13: if cAB is not None: self._cAB = cAB else: if cBC is not None or cAC is not None: raise Exception("Should specify cos(AB) only for" + \ " ibrav = 12 or ibrav = 13" ) self._cBC = self._cAC = 0.0 if ibrav == 14: if cBC is not None: self._cBC = cBC if cAC is not None: self._cAC = cAC if cAB is not None: self._cAB = cAB qeBaseTuple = self._getQEBaseFromParCos(self._ibrav, self._a, self._b, self._c, self._cBC, self._cAC, self._cAB) qeBase = numpy.array(qeBaseTuple[1], dtype = float)*qeBaseTuple[0] self.__primitiveLattice.setLatBase(qeBase) alpha = degrees(acos(self._cBC)) beta = degrees(acos(self._cAC)) gamma = degrees(acos(self._cAB)) #self._standardLattice.setLatPar(self._a,self._b,self._c,alpha,beta,gamma) #self._a0 = a self._base = qeBase if self._qeInput != None and updateInput == True: #self.updatePWInput() self._qeInput.update( forceUpdate = True ) def diffpy(self): '''Returns diffpy.Lattice object. Do not use it for reading QE (standard cell) lattice parameters. Use latticeParams, or a, b, c , ... instead''' return self.__primitiveLattice def updatePWInput(self, qeInput = None): """ Deprecated """ self._qeInput.update( forceUpdate = True ) def setLatticeFromQEVectors(self, ibrav, vectors): """ Will extract conventional lattice parameters from primitive vectors. 'vectors' - is a list with primitive vectors (in QE notation), including lattice parameters. For example from PWSCF output""" from numpy import dot # default values: a = b = c = 1.0 cBC = cAC = cAB = 0.0 v = numpy.array(vectors, dtype = float) if ibrav == 0: self.setLattice(ibrav = 0, a = 1.0, base = vectors ) return #raise NotImplementedError # sc simple cubic: if ibrav == 1: a = v[0,0] if ibrav == 2: a = b = c = sqrt( 2.0*dot(v[0,:],v[0,:]) ) if ibrav == 3: a = b = c = 2.0*sqrt( dot(v[0,:],v[0,:])/3.0) if ibrav == 4: a = b = sqrt( dot(v[0,:],v[0,:])) c = sqrt( dot(v[2,:],v[2,:])) cAB = cosd(120.0) if ibrav == 5: a = b = c = sqrt( dot(v[0,:],v[0,:])) cBC = cAC = cAB = dot(v[0,:],v[2,:])/a**2 if ibrav == 6: a = b = sqrt( dot(v[0,:],v[0,:])) c = sqrt( dot(v[2,:],v[2,:])) if ibrav == 7: a = b = v[1,0] - v[2,0] c = v[1,2] + v[2,2] if ibrav == 8: a = v[0,0] b = v[1,1] c = v[2,2] if ibrav == 9: a = v[0,0] - v[1,0] b = v[0,1] + v[1,1] c = v[2,2] if ibrav == 10: a = v[2,0] - v[0,0] - v[1,0] b = v[2,1] - v[0,1] + v[1,1] c = v[0,2] - v[1,2] + v[2,2] if ibrav == 11: a = v[0,0] - v[1,0] b = v[1,1] - v[2,1] c = v[0,2] - v[2,2] if ibrav == 12: a = v[0,0] b = sqrt( dot(v[1,:],v[1,:])) cAB = v[1,0]/b c = v[2,2] if ibrav == 13: a = v[0,0] + v[2,0] b = sqrt( dot(v[1,:],v[1,:])) c = v[2,2] - v[0,2] cAB = v[1,0]/b if ibrav == 14: a = v[0,0] b = sqrt( dot(v[1,:],v[1,:])) cAB = v[1,0]/b c = sqrt( dot(v[2,:],v[2,:])) cAC = v[2,0]/c cBC = v[2,1]*sqrt(1.0 - cAB**2)/c + cAC*cAB self.setLattice(ibrav, a, b, c, cBC, cAC, cAB) #################################################################### # property handlers #################################################################### # lattice parameters # def _get_a0(self): # if self._a0 != None: # return self._a0 # else: # return self._a # a0 = property(_get_a0, doc ="old lattice parameter a0") def _get_a(self): return self._a def _set_a(self, value): #self._a = value self.setLattice(ibrav = self._ibrav, a = float(value), base = self._base/self._a*value) a = property(_get_a, _set_a, doc ="lattice parameter a") def _get_b(self): return self._b def _set_b(self, value): #self._b = value self.setLattice(ibrav = self._ibrav, b = float(value)) b = property(_get_b, _set_b, doc ="""lattice parameter b""") def _get_c(self): return self._c def _set_c(self, value): #self._c = value self.setLattice(ibrav = self._ibrav, c = float(value)) c = property(_get_c, _set_c, doc ="""lattice parameter c""") def _get_cBC(self): return self._cBC def _set_cBC(self, value): #self._cBC = value self.setLattice(ibrav = self._ibrav, cBC = float(value)) cBC = property(_get_cBC, _set_cBC, doc ="""lattice parameter cBC""") def _get_cAC(self): return self._cAC def _set_cAC(self, value): #self._cAC = value self.setLattice(ibrav = self._ibrav, cAC = float(value)) cAC = property(_get_cAC, _set_cAC, doc ="""lattice parameter cAC""") def _get_cAB(self): return self._cAB def _set_cAB(self, value): #self._cAB = value self.setLattice(ibrav = self._ibrav, cAB = float(value)) cAB = property(_get_cAB, _set_cAB, doc ="""lattice parameter cAB""") def _get_ibrav(self): return self._ibrav def _set_ibrav(self, value): if value < 0: value = 0 ibravOld = self._ibrav self._ibrav = value if value == 0: base = self._base#/self._a if ibravOld != 4: self._type = 'generic cubic' else: self._type = 'generic hexagonal' self.setLattice(ibrav = self._ibrav, a = self._a, base = base) else: if 'generic' in self._type: self._type = 'celldm' base = self._getQEBaseFromParCos( ibrav = self._ibrav, \ a = self._a, b = self._b, c = self._c, cBC = self._cBC, \ cAC = self._cAC, cAB = self._cAB ) qebase = base[0]*numpy.array(base[1]) self.setLatticeFromQEVectors(self._ibrav, qebase) # self.setLattice(self._ibrav) ibrav = property(_get_ibrav, _set_ibrav, doc ="""Lattice symmetry parameter ibrav. Changing it will update the lattice, but leave atomic fractional coordinates unchanged""") def _get_type(self): return self._type def _set_type(self, value): if 'generic' in value: self._type = value self._ibrav = 0 base = self._base #/self._a self.setLattice(ibrav = self._ibrav, a = self._a, base = base) else: if self._ibrav == 0: pass else: self._type = value type = property(_get_type, _set_type, doc ="""QE lattice type: 'celldm', 'traditional' or 'generic cubic', 'generic hexagonal'(implies ibrav = 0""") def _get_base(self): return self._base base = property(_get_base, doc ="""QE base array""") def _getQEBaseFromParCos( self, ibrav = 1, a = 1., b = 1., c = 1., cBC = 0.,cAC = 0. ,cAB = 0.): c_a = float(c)/a # description dictionary of QE base vectors: QEBase = { # sc simple cubic: 1 : (a, [[1, 0, 0], [0, 1, 0], [0, 0, 1]], 'Simple Cubic'), # fcc face centered cubic: 2 : (a/2., [[-1, 0, 1], [0, 1, 1], [-1, 1, 0]], 'Face Centered Cubic'), # bcc body entered cubic: 3 : (a/2., [[1, 1, 1], [-1, 1, 1], [-1, -1, 1]], 'Body Centered Cubic'), # simple hexagonal and trigonal(p): 4 : (a, [[1, 0, 0], [-0.5, sqrt(3.0)/2.0, 0.], [0, 0, c_a]], 'Simple Hexagonal or Trigonal(P)'), # trigonal(r): 5 : (a, [[sqrt((1.-cAB)/2.),-sqrt((1.-cAB)/6.), sqrt((1.+2.*cAB)/3.)], [0, 2.*sqrt((1.-cAB)/6.), sqrt((1.+2.*cAB)/3.)], [-sqrt((1.-cAB)/2.), -sqrt((1.-cAB)/6.), sqrt((1.+2.*cAB)/3.)]], 'Trigonal(R)'), # simple tetragonal (p): 6 : (a, [[1, 0, 0], [0, 1, 0.], [0, 0, c_a]], 'Simple Tetragonal(P)'), # body centered tetragonal (i): 7 : (a/2., [[1, -1, c_a], [1, 1, c_a], [-1, -1, c_a]], 'Body Centered Tetragonal (I)'), # simple orthorhombic (p): 8 : (1.0, [[a, 0., 0.], [0., b, 0.], [0., 0., c]], 'Simple Orthorhombic (P)'), # bco base centered orthorhombic: 9: (1.0, [[a/2., b/2., 0.], [-a/2., b/2., 0.], [0., 0., c]], 'Base Centered Orthorhombic'), # face centered orthorhombic: 10: (1.0, [[a/2., 0., c/2.], [a/2., b/2., 0.], [0., b/2., c/2.]], 'Face Centered Orthorhombic' ), # body centered orthorhombic: 11: (1.0, [[a/2., b/2., c/2.], [-a/2., b/2., c/2.], [-a/2., -b/2., c/2.]], 'Body Centered Orthorhombic'), # monoclinic (p): 12: (1.0, [[a, 0, 0], [b*cAB, b*sqrt(1.0 - cAB**2), 0], [0, 0, c]], 'Monoclinic (P)'), # base centered monoclinic: 13: (1.0, [[a/2., 0, -c/2.], [b*cAB, b*sqrt(1.0 - cAB**2), 0], [a/2., 0, c/2.]], 'Base Centered Monoclinic'), # triclinic: 14: (1.0, [[a, 0, 0], [b*cAB, b*sqrt(1.0 - cAB**2), 0], [c*cAC, c*( cBC-cAC*cAB )/sqrt(1.-cAB**2), c*sqrt( 1. + 2.*cBC*cAC*cAB - cBC**2 - cAC**2 - cAB**2)/sqrt(1.-cAB**2)]], 'Triclinic') } return QEBase[ibrav]
#!/usr/bin/env python import math import numpy # To change this template, choose Tools | Templates # and open the template in the editor. from diffpy.Structure.structure import Structure from diffpy.Structure.lattice import Lattice myAtoms = ['Mg', 'B', 'B'] myLattice = Lattice() theta = -30. * math.pi / 180. rotMatrix = [[math.cos(theta), -math.sin(theta), 0], [math.sin(theta), math.cos(theta), 0], [0, 0, 1]] #myLattice.setLatPar(1., 1, 1.,gamma=120.0)#,baserot = rotMatrix) print myLattice.base a = 5.2 c = 6.3 base = [[1, 0, 0], [-1. / 2., math.sqrt(3.) / 2., 0.], [0, 0, c / a]] myLattice.setLatBase(base) print myLattice.base print "baserot:" print myLattice.baserot myLattice.setLatPar(5.2, 5.2, 6.3, gamma=120.0, baserot=myLattice.baserot) print myLattice.base #myLattice.setLatBase(numpy.array(base)*a) #print myLattice #print myLattice.abcABG()
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 = [])
#!/usr/bin/env python import math import numpy # To change this template, choose Tools | Templates # and open the template in the editor. from diffpy.Structure.structure import Structure from diffpy.Structure.lattice import Lattice myAtoms = ['Mg', 'B', 'B'] myLattice = Lattice() theta = -30.*math.pi/180. rotMatrix = [[math.cos(theta),-math.sin(theta),0],[math.sin(theta),math.cos(theta),0],[0,0,1]] #myLattice.setLatPar(1., 1, 1.,gamma=120.0)#,baserot = rotMatrix) print myLattice.base a = 5.2 c = 6.3 base = [[1,0,0],[-1./2.,math.sqrt(3.)/2.,0.],[0,0,c/a]] myLattice.setLatBase(base) print myLattice.base print "baserot:" print myLattice.baserot myLattice.setLatPar(5.2, 5.2, 6.3,gamma=120.0,baserot = myLattice.baserot) print myLattice.base #myLattice.setLatBase(numpy.array(base)*a) #print myLattice #print myLattice.abcABG()