예제 #1
0
class P_discus(StructureParser):
    """Parser for DISCUS structure format.  The parser chokes
    on molecule and generator records.
    """

    def __init__(self):
        StructureParser.__init__(self)
        self.format = "discus"
        # helper variables
        self.nl = None
        self.lines = None
        self.line = None
        self.stru = None
        self.ignored_lines = []
        self.cell_read = False
        self.ncell_read = False
        return

    def parseLines(self, lines):
        """Parse list of lines in DISCUS format.

        Return PDFFitStructure instance or raise StructureFormatError.
        """
        self.lines = lines
        ilines = self._linesIterator()
        self.stru = PDFFitStructure()
        record_parsers = {
            "cell" : self._parse_cell,
            "format" : self._parse_format,
            "generator" : self._parse_not_implemented,
            "molecule" : self._parse_not_implemented,
            "ncell" : self._parse_ncell,
            "spcgr" : self._parse_spcgr,
            "symmetry" : self._parse_not_implemented,
            "title" : self._parse_title,
            "shape" : self._parse_shape,
        }
        try:
            # parse header
            for self.line in ilines:
                words = self.line.split()
                if not words or words[0][0] == '#': continue
                if words[0] == 'atoms':             break
                rp = record_parsers.get(words[0], self._parse_unknown_record)
                rp(words)
            # check if cell has been defined
            if not self.cell_read:
                emsg = "%d: unit cell not defined" % self.nl
                raise StructureFormatError(emsg)
            # parse atoms
            for self.line in ilines:
                words = self.line.replace(',', ' ').split()
                if not words or words[0][0] == '#': continue
                self._parse_atom(words)
            # self consistency check
            exp_natoms = reduce(lambda x,y : x*y, self.stru.pdffit['ncell'])
            # only check if ncell record exists
            if self.ncell_read and exp_natoms != len(self.stru):
                emsg = 'Expected %d atoms, read %d.' % \
                    (exp_natoms, len(self.stru))
                raise StructureFormatError(emsg)
            # take care of superlattice
            if self.stru.pdffit['ncell'][:3] != [1,1,1]:
                latpars = list(self.stru.lattice.abcABG())
                superlatpars = [ latpars[i]*self.stru.pdffit['ncell'][i]
                                 for i in range(3) ] + latpars[3:]
                superlattice = Lattice(*superlatpars)
                self.stru.placeInLattice(superlattice)
                self.stru.pdffit['ncell'] = [1, 1, 1, exp_natoms]
        except (ValueError, IndexError):
            exc_type, exc_value, exc_traceback = sys.exc_info()
            emsg = "%d: file is not in DISCUS format" % self.nl
            raise StructureFormatError, emsg, exc_traceback
        return self.stru
    # End of parseLines

    def toLines(self, stru):
        """Convert Structure stru to a list of lines in DISCUS format.
        Return list of strings.
        """
        self.stru = stru
        # if necessary, convert self.stru to PDFFitStructure
        if not isinstance(stru, PDFFitStructure):
            self.stru = PDFFitStructure(stru)
        # build the stru_pdffit dictionary initialized from the defaults
        # in PDFFitStructure
        stru_pdffit = PDFFitStructure().pdffit
        if stru.pdffit:
            stru_pdffit.update(stru.pdffit)
        # here we can start
        self.lines = lines = []
        lines.append( "title   " + self.stru.title.strip() )
        lines.append( "spcgr   " + stru_pdffit["spcgr"] )
        if stru_pdffit.get('spdiameter', 0.0) > 0.0:
            line = 'shape   sphere, %g' % stru_pdffit['spdiameter']
            lines.append(line)
        if stru_pdffit.get('stepcut', 0.0) > 0.0:
            line = 'shape   stepcut, %g' % stru_pdffit['stepcut']
            lines.append(line)
        lines.append( "cell   %9.6f, %9.6f, %9.6f, %9.6f, %9.6f, %9.6f" %
                self.stru.lattice.abcABG() )
        lines.append( "ncell  %9i, %9i, %9i, %9i" % (1, 1, 1, len(self.stru)) )
        lines.append( "atoms" )
        for a in self.stru:
            lines.append( "%-4s %17.8f %17.8f %17.8f %12.4f" % (
                a.element.upper(), a.xyz[0], a.xyz[1], a.xyz[2], a.Bisoequiv))
        return lines
    # End of toLines

    def _linesIterator(self):
        """Iterator over self.lines, which increments self.nl
        """
        # ignore trailing empty lines
        stop = len(self.lines)
        while stop > 0 and self.lines[stop-1].strip() == "":
            stop -= 1
        self.nl = 0
        # read header of PDFFit file
        for self.line in self.lines[:stop]:
            self.nl += 1
            yield self.line
        # end of _linesIterator

    def _parse_cell(self, words):
        """Process the cell record from DISCUS structure file.
        """
        # split again on spaces or commas
        words = self.line.replace(',', ' ').split()
        latpars = [ float(w) for w in words[1:7] ]
        try:
            self.stru.lattice.setLatPar(*latpars)
        except ZeroDivisionError:
            emsg = "%d: Invalid lattice parameters - zero cell volume" % \
                    self.nl
            raise StructureFormatError(emsg)
        self.cell_read = True
        return

    def _parse_format(self, words):
        """Process the format record from DISCUS structure file.
        """
        if words[1] == 'pdffit':
            emsg = "%d: file is not in DISCUS format" % self.nl
            raise StructureFormatError(emsg)
        return

    def _parse_ncell(self, words):
        """Process the ncell record from DISCUS structure file.
        """
        # split again on spaces or commas
        words = self.line.replace(',', ' ').split()
        self.stru.pdffit['ncell'] = [ int(w) for w in words[1:5] ]
        self.ncell_read = True
        return

    def _parse_spcgr(self, words):
        """Process the spcgr record from DISCUS structure file.
        """
        self.stru.pdffit['spcgr'] = ''.join(words[1:])
        return

    def _parse_title(self, words):
        """Process the title record from DISCUS structure file.
        """
        self.stru.title = self.line.lstrip()[5:].strip()
        return

    def _parse_shape(self, words):
        """Process the shape record from DISCUS structure file.
        """
        # strip away any commas
        linefixed = " ".join(words).replace(',', ' ')
        wordsfixed = linefixed.split()
        shapetype = wordsfixed[1]
        if shapetype == 'sphere':
            self.stru.pdffit['spdiameter'] = float(words[2])
        elif shapetype == 'stepcut':
            self.stru.pdffit['stepcut'] = float(words[2])
        else:
            emsg = 'Invalid type of particle shape correction %r' % shapetype
            raise StructureFormatError, emsg
        return

    def _parse_atom(self, words):
        """Process atom records in DISCUS structure file.
        """
        element = words[0][0:1].upper() + words[0][1:].lower()
        xyz = [float(w) for w in words[1:4]]
        Biso = float(words[4])
        self.stru.addNewAtom(element, xyz)
        a = self.stru.getLastAtom()
        a.Bisoequiv = Biso
        return

    def _parse_unknown_record(self, words):
        """Process unknown record in DISCUS structure file.
        Silently ignores the line and adds it to self.ignored_lines
        Raises StructureFormatError.
        """
        self.ignored_lines.append(self.line)
        return

    def _parse_not_implemented(self, words):
        """Process the unimplemented records from DISCUS structure file.
        Raises NotImplementedError.
        """
        emsg = "%d: reading of DISCUS record %r is not implemented." % \
                (self.nl, words[0])
        raise NotImplementedError(emsg)