def _expandAsymmetricUnit(self): """Perform symmetry expansion of self.stru using self.spacegroup. This method updates data in stru and eau. No return value. """ from diffpy.structure.symmetryutilities import ExpandAsymmetricUnit # get reverse-ordered unique indices corepos = [a.xyz for a in self.stru] coreUijs = [a.U for a in self.stru] self.eau = ExpandAsymmetricUnit(self.spacegroup, corepos, coreUijs, eps=self.eps) # build a nested list of new atoms: newatoms = [] for i, ca in enumerate(self.stru): eca = [] # expanded core atom for j in range(self.eau.multiplicity[i]): a = Atom(ca) a.xyz = self.eau.expandedpos[i][j] if j > 0: a.label += '_' + str(j + 1) if a.anisotropy: a.U = self.eau.expandedUijs[i][j] eca.append(a) newatoms.append(eca) # insert new atoms where they belong self.stru[:] = sum(newatoms, []) return
def supercell(S, mno): """Perform supercell expansion for a structure. New lattice parameters are multiplied and fractional coordinates divided by corresponding multiplier. New atoms are grouped with their source in the original cell. S -- an instance of Structure from diffpy.structure. mno -- sequence of 3 integers for cell multipliers along the a, b and c axes. Return a new expanded structure instance. Raise TypeError when S is not Structure instance. Raise ValueError for invalid mno argument. """ # check arguments if len(mno) != 3: emsg = "Argument mno must contain 3 numbers." raise ValueError(emsg) elif min(mno) < 1: emsg = "Multipliers must be greater or equal 1" raise ValueError(emsg) if not isinstance(S, Structure): emsg = "The first argument must be a Structure instance." raise TypeError(emsg) # convert mno to a tuple of integers so it can be used as range limit. mno = (int(mno[0]), int(mno[1]), int(mno[2])) # create return instance newS = Structure(S) if mno == (1, 1, 1): return newS # back to business ijklist = [(i,j,k) for i in range(mno[0]) for j in range(mno[1]) for k in range(mno[2])] # numpy.floor returns float array mnofloats = numpy.array(mno, dtype=float) # build a list of new atoms newAtoms = [] for a in S: for ijk in ijklist: adup = Atom(a) adup.xyz = (a.xyz + ijk)/mnofloats newAtoms.append(adup) # newS can own references in newAtoms, no need to make copies newS.__setitem__(slice(None), newAtoms, copy=False) # take care of lattice parameters newS.lattice.setLatPar( a=mno[0]*S.lattice.a, b=mno[1]*S.lattice.b, c=mno[2]*S.lattice.c ) return newS
def supercell(S, mno): """Perform supercell expansion for a structure. New lattice parameters are multiplied and fractional coordinates divided by corresponding multiplier. New atoms are grouped with their source in the original cell. S -- an instance of Structure from diffpy.structure. mno -- sequence of 3 integers for cell multipliers along the a, b and c axes. Return a new expanded structure instance. Raise TypeError when S is not Structure instance. Raise ValueError for invalid mno argument. """ # check arguments if len(mno) != 3: emsg = "Argument mno must contain 3 numbers." raise ValueError(emsg) elif min(mno) < 1: emsg = "Multipliers must be greater or equal 1" raise ValueError(emsg) if not isinstance(S, Structure): emsg = "The first argument must be a Structure instance." raise TypeError(emsg) # convert mno to a tuple of integers so it can be used as range limit. mno = (int(mno[0]), int(mno[1]), int(mno[2])) # create return instance newS = Structure(S) if mno == (1, 1, 1): return newS # back to business ijklist = [(i, j, k) for i in range(mno[0]) for j in range(mno[1]) for k in range(mno[2])] # numpy.floor returns float array mnofloats = numpy.array(mno, dtype=float) # build a list of new atoms newAtoms = [] for a in S: for ijk in ijklist: adup = Atom(a) adup.xyz = (a.xyz + ijk) / mnofloats newAtoms.append(adup) # newS can own references in newAtoms, no need to make copies newS.__setitem__(slice(None), newAtoms, copy=False) # take care of lattice parameters newS.lattice.setLatPar(a=mno[0] * S.lattice.a, b=mno[1] * S.lattice.b, c=mno[2] * S.lattice.c) return newS
def expandAsymmetricUnit(self, spacegroup, indices, sgoffset=[0,0,0]): """Perform symmetry expansion for atoms at given indices. Temperature factors may be corrected to reflect the symmetry. All constraints for expanded atoms are erased with the exception of the occupancy("occ". Constraints of unaffected atoms are adjusted for new positions self.initial. spacegroup -- instance of SpaceGroup from diffpy.structure indices -- list of integer indices of atoms to be expanded sgoffset -- optional offset of space group origin [0,0,0] """ from diffpy.structure.symmetryutilities import ExpandAsymmetricUnit acd = self._popAtomConstraints() # get unique, reverse sorted indices ruindices = dict.fromkeys(indices).keys() ruindices.sort() ruindices.reverse() coreatoms = [self.initial[i] for i in ruindices] corepos = [a.xyz for a in coreatoms] coreUijs = [a.U for a in coreatoms] eau = ExpandAsymmetricUnit(spacegroup, corepos, coreUijs, sgoffset=sgoffset, eps=self.symposeps) # build a nested list of new atoms: newatoms = [] for i in range(len(coreatoms)): ca = coreatoms[i] caocc_con = None if ca in acd and "occ" in acd[ca]: caocc_con = acd[ca]["occ"] eca = [] # expanded core atom for j in range(eau.multiplicity[i]): a = Atom(ca) a.xyz = eau.expandedpos[i][j] a.U = eau.expandedUijs[i][j] eca.append(a) if caocc_con is None: continue # make a copy of occupancy constraint acd[a] = {"occ" : copy.copy(caocc_con)} newatoms.append(eca) # insert new atoms where they belong for i, atomlist in zip(ruindices, newatoms): self.initial[i:i+1] = atomlist # remember this spacegroup as the last one used self.initial.pdffit["spcgr"] = spacegroup.short_name self.initial.pdffit["sgoffset"] = list(sgoffset) # tidy constraints self._restoreAtomConstraints(acd) return
def _expandAsymmetricUnit(self, block): """Perform symmetry expansion of self.stru using self.spacegroup. This method updates data in stru and eau. Parameters ---------- block : CifBlock The top-level block containing crystal structure data. """ from diffpy.structure.symmetryutilities import ExpandAsymmetricUnit corepos = [a.xyz for a in self.stru] coreUijs = [a.U for a in self.stru] self.eau = ExpandAsymmetricUnit(self.spacegroup, corepos, coreUijs, eps=self.eps) # setup anisotropy according to symmetry requirements # unless it was already explicitly set for ca, uisotropy in zip(self.stru, self.eau.Uisotropy): if not ca.label in self.anisotropy: ca.anisotropy = not uisotropy self.anisotropy[ca.label] = ca.anisotropy # build a nested list of new atoms: newatoms = [] for i, ca in enumerate(self.stru): eca = [] # expanded core atom for j in range(self.eau.multiplicity[i]): a = Atom(ca) a.xyz = self.eau.expandedpos[i][j] if j > 0: a.label += '_' + str(j + 1) if a.anisotropy: a.U = self.eau.expandedUijs[i][j] eca.append(a) newatoms.append(eca) # insert new atoms where they belong self.stru[:] = sum(newatoms, []) return
def expandSuperCell(self, mno): """Perform supercell expansion for this structure and adjust constraints for positions and lattice parameters. New lattice parameters are multiplied and fractional coordinates divided by corresponding multiplier. New atoms are grouped with their source in the original cell. mno -- tuple or list of three positive integer cell multipliers along the a, b, c axis """ # check argument if tuple(mno) == (1, 1, 1): return if min(mno) < 1: raise ControlValueError("mno must contain 3 positive integers") # back to business acd = self._popAtomConstraints() mnofloats = numpy.array(mno[:3], dtype=float) ijklist = [(i,j,k) for i in range(mno[0]) for j in range(mno[1]) for k in range(mno[2])] # build a list of new atoms newatoms = [] for a in self.initial: for ijk in ijklist: adup = Atom(a) adup.xyz = (a.xyz + ijk)/mnofloats newatoms.append(adup) # does atom a have any constraint? if a not in acd: continue # add empty constraint dictionary for duplicate atom acd[adup] = {} for barevar, con in acd[a].iteritems(): formula = con.formula if barevar in ("x", "y", "z"): symidx = "xyz".index(barevar) if ijk[symidx] != 0: formula += " + %i" % ijk[symidx] if mno[symidx] > 1: formula = "(%s)/%.1f" % (formula, mno[symidx]) formula = re.sub(r'\((@\d+)\)', r'\1', formula) # keep other formulas intact and add constraint # for barevar of the duplicate atom acd[adup][barevar] = Constraint(formula) # replace original atoms with newatoms self.initial[:] = newatoms for ai, an in zip(self.initial, newatoms): if an in acd: acd[ai] = acd[an] # and rebuild their constraints self._restoreAtomConstraints(acd) # take care of lattice parameters self.initial.lattice.setLatPar( a=mno[0]*self.initial.lattice.a, b=mno[1]*self.initial.lattice.b, c=mno[2]*self.initial.lattice.c ) # adjust lattice constraints if present latvars = ( "lat(1)", "lat(2)", "lat(3)" ) for var, multiplier in zip(latvars, mno): if var in self.constraints and multiplier > 1: con = self.constraints[var] formula = "%.0f*(%s)" % (multiplier, con.formula) formula = re.sub(r'\((@\d+)\)', r'\1', formula) con.formula = formula return