Example #1
0
 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)
Example #2
0
    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() )
Example #3
0
    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
Example #4
0
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)
Example #5
0
    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)
Example #6
0
    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)
Example #7
0
 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
Example #8
0
    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
Example #9
0
 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
Example #10
0
    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
Example #11
0
    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)
Example #12
0
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
Example #13
0
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""")
Example #14
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)
Example #15
0
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
Example #16
0
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]
Example #17
0
#!/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()
Example #18
0
    def _setReducedStructureFromDiffpyStructure(self,
                                                structure,
                                                ibrav,
                                                massList=[],
                                                psList=[]):
        """
        structure - diffpy.Structure object
        ibrav - Lattice index
        psList - list of strings with potential names
        diffpyStructure object will be modified with reduced atomic positions
        """
        import copy

        diffpyLattice = copy.deepcopy(structure.lattice)

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

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

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

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

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

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

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

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

        self[:] = []

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

        for atom in reducedStructure:
            elem = self._element(atom)
            self.addNewAtom(atype = elem, xyz = atom.xyz, \
                            mass = atomicSpecies[elem][0], \
                            potential = atomicSpecies[elem][1],\
                            lattice = self.lattice, optConstraint = [])
#!/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()