def checkConfiguration(self):
        """Verify that the dopant and base are elements.

        More detailed checking is done in the control method.
        """
        from diffpy.pdfgui.control.controlerrors import ControlValueError
        from diffpy.pdffit2 import is_element
        base = self.textCtrlBaseElement.GetValue()
        dopant = self.textCtrlDopant.GetValue()
        # Make sure that the base and dopant are actual elements
        base = base.title()
        dopant = dopant.title()
        if not is_element(base):
            raise ControlValueError("'%s' is not an element!" % base)
        if not is_element(dopant):
            raise ControlValueError("'%s' is not an element!" % dopant)
        return
示例#2
0
    def _parseAtomSelectionString(self, s):
        '''Process string that describes a set of atoms in the structure.

        s    -- selection string formatted as [!]{element|indexOrRange|all}
                "!" negates the selection, indexOrRange can be 1, 1:4,
                where atom indices starts from 1, and "all" matches all atoms.

        Return a dictionary with following keys:
        'fixedstring'    -- selection string adjusted to standard formatting
        'flags'          -- dictionary of atom indices and boolean flags for
                            normal or negated selection.
        Raise ControlValueError for invalid string format.
        '''
        # delayed initialization of the class variable
        if self._rxatomselection is None:
            FitStructure._rxatomselection = re.compile(
                r'''
            (?P<negate>!?)                      # exclamation point
            (?:(?P<element>[a-zA-Z]+)$|         # element|all   or
               (?P<start>\d+)(?P<stop>:\d+)?$   # number range
            )''', re.VERBOSE)
        assert self._rxatomselection
        Natoms = len(self.initial)
        flags = {}
        rv = {'fixedstring': '', 'flags': flags}
        # allow empty string and return an empty flags dictionary
        s1 = s.replace(' ', '')
        if not s1: return rv
        mx = self._rxatomselection.match(s1)
        if not mx:
            emsg = "Invalid selection syntax in '%s'" % s
            raise ControlValueError(emsg)
        if mx.group('negate'):
            rv['fixedstring'] = '!'
        flg = not mx.group('negate')
        # process atom type
        if mx.group('element'):
            elfixed = mx.group('element')
            elfixed = elfixed[0:1].upper() + elfixed[1:].lower()
            if elfixed == 'All':
                flags.update(dict.fromkeys(range(Natoms), flg))
                rv['fixedstring'] += elfixed.lower()
            else:
                for idx in range(Natoms):
                    if self.initial[idx].element == elfixed:
                        flags[idx] = flg
                rv['fixedstring'] += elfixed
        # process range
        else:
            lo = max(int(mx.group('start')) - 1, 0)
            rv['fixedstring'] += mx.group('start')
            hi = lo + 1
            if mx.group('stop'):
                hi = int(mx.group('stop')[1:])
                rv['fixedstring'] += mx.group('stop')
            hi = min(hi, Natoms)
            flags.update(dict.fromkeys(range(lo, hi), flg))
        return rv
示例#3
0
    def setRGrid(self, rmin=None, rstep=None, rmax=None):
        """Change specified r-grid parameters (rmin, rstep, rmax).
        Adjust rmax for integer number of steps.

        rmin  -- new low rcalc boundary
        rstep -- new r-grid step
        rmax  -- new maximum rcalc, slightly adjusted to accommodate rstep

        No return value.
        Raise ControlValueError for invalid range specification.
        """
        if rmin is None: rmin = self.rmin
        if rstep is None: rstep = self.rstep
        if rmax is None: rmax = self.rmax
        rstep = float(rstep)
        # check if arguments are valid
        if not rmin > 0:
            emsg = "Low range boundary must be positive."
            raise ControlValueError(emsg)
        if not rmin < rmax:
            emsg = "Invalid range boundaries."
            raise ControlValueError(emsg)
        if rstep <= 0.0:
            emsg = "Invalid value of rstep, rstep must be positive."
            raise ControlValueError(emsg)
        # find number of r bins
        nbins = int(math.ceil((rmax - rmin) / rstep))
        # check for overshot due to round-off
        epsilonr = 1.0e-8 * rstep
        deltarmax = abs(rmin + (nbins - 1) * rstep - rmax)
        if nbins > 1 and deltarmax < epsilonr:
            nbins -= 1
        # All went well, let us go ahead and set the attributes.
        self.rmin = rmin
        self.rstep = rstep
        self.rmax = rmin + nbins * rstep
        self.rlen = nbins + 1
        return
    def refresh(self):
        """Refresh wigets on the panel."""
        if self.structure is None:
            raise ValueError("structure is not defined.")

        self.refreshTextCtrls()

        ### update the grid ###
        natoms = len(self.structure)
        nrows = self.gridAtoms.GetNumberRows()
        self.gridAtoms.BeginBatch()
        # make sure grid has correct number of rows
        if natoms > nrows:
            self.gridAtoms.InsertRows(numRows=natoms - nrows)
        elif natoms < nrows:
            self.gridAtoms.DeleteRows(numRows=nrows - natoms)

        # start with clean grid
        self.gridAtoms.ClearGrid()

        # fill the first 'elem' column with element symbols
        for row, atom in zip(range(natoms), self.structure):
            self.gridAtoms.SetCellValue(row, 0, atom.element)

        # update constraints
        bareAtomVarColumn = dict(
            zip(self.lAtomConstraints, range(1,
                                             1 + len(self.lAtomConstraints))))
        avpat = re.compile(r'(\w+)\((\d+)\)$')
        for var, con in self.constraints.iteritems():
            m = avpat.match(var)
            if not m: continue
            barevar = m.group(1)
            if not barevar in bareAtomVarColumn: continue
            column = bareAtomVarColumn[barevar]
            row = int(m.group(2)) - 1
            if not 0 <= row < natoms:
                emsg = "Invalid variable index for %r" % var
                raise ControlValueError(emsg)
            self.gridAtoms.SetCellValue(row, column, con.formula)
            barevar = re.sub(r'\(\d+\)$', '', var)
            if not barevar in bareAtomVarColumn: continue

        self.gridAtoms.AutosizeLabels()
        self.gridAtoms.AutoSizeColumns()
        self.gridAtoms.EndBatch()

        self.gridAtoms.AdjustScrollbars()
        self.gridAtoms.ForceRefresh()
        return
示例#5
0
    def updateWidgets(self):
        """Update the widgets."""
        # Update space group
        sgname = self.sgComboBox.GetValue()
        try:
            self.spacegroup = self.structure.getSpaceGroup(sgname)
            error = None
        except ValueError:
            error = "Space group %s does not exist." % sgname
        # This changes list box value to the short_name of the new spacegroup
        # or to the name of previous spacegroup when getSpaceGroup failed.
        self.sgComboBox.SetValue(self.spacegroup.short_name)

        # Update offset
        for i in range(3):
            textctrl = self.textCtrls[i]
            val = textctrl.GetValue()
            # make sure the value is meaningful
            try:
                val = float(eval("1.0*" + val, dict(math.__dict__)))
            except (NameError, TypeError, SyntaxError):
                val = 0.0
            textctrl.SetValue("%s" % val)
            self.offset[i] = val

        # find how many new atoms would be generated
        from diffpy.Structure.SymmetryUtilities import ExpandAsymmetricUnit
        corepos = [self.structure[i].xyz for i in self.indices]
        symposeps = self.structure.symposeps
        eau = ExpandAsymmetricUnit(self.spacegroup,
                                   corepos,
                                   sgoffset=self.offset,
                                   eps=symposeps)
        newsize = sum(eau.multiplicity)
        s = ""
        if len(self.indices) != 1:
            s = "s"
        message = "%i atom%s selected.  Expanding to %i positions." %\
                (len(self.indices), s, newsize)
        self.numConstrainedLabel.SetLabel(message)

        # Raise an error if we had to change the space group
        if error:
            raise ControlValueError(error)
        return
示例#6
0
    def getPairSelectionFlags(self, s=None):
        """Translate string s to a list of allowed values for first and second
        pair index.  Raise ControlValueError for invalid syntax of s.  See
        setSelectedPairs() docstring for a definition of pair selection syntax.

        s -- string describing selected pairs (default: self.selected_pairs)

        Return a dictionary with following keys:

        firstflags  -- list of selection flags for first indices
        secondflags -- list of selection flags for second indices
        fixed_pair_string -- argument corrected to standard syntax
        """
        if s is None: s = self.selected_pairs
        Natoms = len(self.initial)
        # sets of first and second indices
        firstflags = Natoms * [False]
        secondflags = Natoms * [False]
        # words of fixed_pair_string
        words_fixed = []
        s1 = s.strip(' \t,')
        words = re.split(r' *, *', s1)
        for w in words:
            wparts = w.split('-')
            if len(wparts) != 2:
                emsg = "Selection word '%s' must contain one dash '-'." % w
                raise ControlValueError(emsg)
            sel0 = self._parseAtomSelectionString(wparts[0])
            sel1 = self._parseAtomSelectionString(wparts[1])
            wfixed = sel0['fixedstring'] + '-' + sel1['fixedstring']
            words_fixed.append(wfixed)
            for idx, flg in sel0['flags'].items():
                firstflags[idx] = flg
            for idx, flg in sel1['flags'].items():
                secondflags[idx] = flg
        # build returned dictionary
        rv = {
            'firstflags': firstflags,
            'secondflags': secondflags,
            'fixed_pair_string': ", ".join(words_fixed),
        }
        return rv
示例#7
0
    def updateWidgets(self):
        """Update the widgets."""
        # Update space group
        sgname = self.sgComboBox.GetValue()
        try:
            self.spacegroup = self.structure.getSpaceGroup(sgname)
            error = None
        except ValueError:
            error = "Space group %s does not exist." % sgname
        # This changes list box value to the short_name of the new spacegroup
        # or to the name of previous spacegroup when getSpaceGroup failed.
        self.sgComboBox.SetValue(self.spacegroup.short_name)
        # Update offset
        for i in range(3):
            textctrl = self.textCtrls[i]
            val = textctrl.GetValue()
            # make sure the value is meaningful
            try:
                val = float(eval("1.0*"+val, dict(math.__dict__)))
            except (NameError, TypeError, SyntaxError):
                val = 0
            textctrl.SetValue("%s"%val)
            self.offset[i] = val

        #newatoms = len(stemp) - len(self.structure)
        s = ""
        if len(self.indices) != 1:
            s = "s"
        message = "%i atom%s selected." %\
                (len(self.indices), s)
        self.numConstrainedLabel.SetLabel(message)

        # Raise an error if we had to change the space group
        if error:
            raise ControlValueError(error);
        return
示例#8
0
def makeDopingSeries(control, fit, base, dopant, paths, doping):
    """Make a temperature series.

    control         --  pdguicontrol instance
    fit             --  The template fit
    base            --  Name of the base element
    dopant          --  Name of the dopant element
    paths           --  list of path names of new datasets
    doping          --  list of doping values corresponding to the datasets

    returns a list of the new fit organization objects
    """
    from diffpy.pdffit2 import is_element

    # Make sure that base and dopant are elements
    base = base.title()
    dopant = dopant.title()
    if not is_element(base):
        raise ControlValueError("'%s' is not an element!" % base)
    if not is_element(dopant):
        raise ControlValueError("'%s' is not an element!" % dopant)

    # Make sure that base and dopant are in the structure file(s)
    hasBase = False
    hasDopant = False
    for S in fit.strucs:
        for atom in S:
            if atom.element == base:
                hasBase = True
            if atom.element == dopant:
                hasDopant = True
        if hasBase and hasDopant: break

    if not hasBase:
        message = "The template structure does not contain the base atom."
        raise ControlValueError(message)

    if not hasDopant:
        message = "The template structure does not contain the dopant atom."
        raise ControlValueError(message)

    # Make sure we're only replacing a single dataset
    if len(fit.datasets) != 1:
        message = "Can't apply macro to fits with multiple datasets."
        raise ControlValueError(message)

    fits = []
    # holds all of the other information about the dataset
    fitbasename = fit.name
    fitnewname = fit.name
    fitlastname = fit.name
    dataset = fit.datasets[0]
    for i in range(len(paths)):
        filename = paths[i]
        fitlastname = fitnewname

        fitcopy = control.copy(fit)

        # Get rid of the old dataset
        temp = fitcopy.datasets[0]
        fitcopy.remove(temp)

        # Configure the new dataset
        dsname = os.path.basename(filename)
        newdataset = FitDataSet(dsname)
        newdataset.readObs(filename)

        newdataset.qdamp = dataset.qdamp
        newdataset.qbroad = dataset.qbroad
        newdataset.dscale = dataset.dscale
        newdataset.fitrmin = dataset.fitrmin
        newdataset.fitrmax = dataset.fitrmax
        rstep = dataset.fitrstep
        st = dataset.getFitSamplingType()
        newdataset.setFitSamplingType(st, rstep)
        temperature = dataset.metadata.get("temperature")
        if temperature is None: temperature = 300.0
        newdataset.metadata["temperature"] = temperature
        newdataset.constraints = copy.deepcopy(dataset.constraints)

        # Set the chosen temperature
        newdataset.metadata["doping"] = doping[i]

        # Add the dataset to the fitcopy
        fitcopy.add(newdataset, None)

        # Update the doping information in the structures
        for S in fitcopy.strucs:
            for A in S:
                if A.element == dopant:
                    A.occupancy = doping[i]
                if A.element == base:
                    A.occupancy = 1 - doping[i]

        # Set the parameters to the previous fit's name, if one exists.
        if fitlastname:
            parval = "=%s" % fitlastname
            for par in fitcopy.parameters.values():
                par.setInitial(parval)

        # Now paste the copy into the control.
        fitnewname = "%s-%1.4f" % (fitbasename, doping[i])
        o = control.paste(fitcopy, new_name=fitnewname)
        fits.append(o)

    return [f.organization() for f in fits]
示例#9
0
def makeTemperatureSeries(control, fit, paths, temperatures):
    """Make a temperature series.

    control         --  pdguicontrol instance
    fit             --  The template fit
    paths           --  list of path names of new datasets
    temperatures    --  list of temperatures corresponding to the datasets

    returns a list of the new fit organization objects
    """

    if len(fit.datasets) != 1:
        message = "Can't apply macro to fits with multiple datasets."
        raise ControlValueError(message)

    fits = []
    # holds all of the other information about the dataset
    fitbasename = fit.name
    fitnewname = fit.name
    fitlastname = fit.name
    dataset = fit.datasets[0]
    for i in range(len(paths)):
        filename = paths[i]
        fitlastname = fitnewname

        fitcopy = control.copy(fit)

        # Get rid of the old dataset
        temp = fitcopy.datasets[0]
        fitcopy.remove(temp)

        # Configure the new dataset
        dsname = os.path.basename(filename)
        newdataset = FitDataSet(dsname)
        newdataset.readObs(filename)

        newdataset.qdamp = dataset.qdamp
        newdataset.qbroad = dataset.qbroad
        newdataset.dscale = dataset.dscale
        newdataset.fitrmin = dataset.fitrmin
        newdataset.fitrmax = dataset.fitrmax
        rstep = dataset.fitrstep
        st = dataset.getFitSamplingType()
        newdataset.setFitSamplingType(st, rstep)
        doping = dataset.metadata.get("doping")
        if doping is None: doping = 0.0
        newdataset.metadata["doping"] = doping
        newdataset.constraints = copy.deepcopy(dataset.constraints)

        # Set the chosen temperature
        newdataset.metadata["temperature"] = temperatures[i]

        # Add the dataset to the fitcopy
        fitcopy.add(newdataset, None)

        # Set the parameters to the previous fit's name, if one exists.
        if fitlastname:
            parval = "=%s" % fitlastname
            for par in fitcopy.parameters.values():
                par.setInitial(parval)

        # Now paste the copy into the control.
        fitnewname = "%s-T%i=%g" % (fitbasename, i + 1, temperatures[i])
        o = control.paste(fitcopy, new_name=fitnewname)
        fits.append(o)

    return [f.organization() for f in fits]
示例#10
0
    def expandSuperCell(self, mno):
        """Perform supercell expansion for this structure and adjust
        constraints for positions and lattice parameters.  New lattice
        parameters are multiplied and fractional coordinates divided by
        corresponding multiplier.  New atoms are grouped with their source
        in the original cell.

        mno -- tuple or list of three positive integer cell multipliers along
        the a, b, c axis
        """
        # check argument
        if tuple(mno) == (1, 1, 1):     return
        if min(mno) < 1:
            raise ControlValueError("mno must contain 3 positive integers")
        # back to business
        acd = self._popAtomConstraints()
        mnofloats = numpy.array(mno[:3], dtype=float)
        ijklist = [(i,j,k) for i in range(mno[0])
                    for j in range(mno[1]) for k in range(mno[2])]
        # build a list of new atoms
        newatoms = []
        for a in self.initial:
            for ijk in ijklist:
                adup = Atom(a)
                adup.xyz = (a.xyz + ijk)/mnofloats
                newatoms.append(adup)
                # does atom a have any constraint?
                if a not in acd:    continue
                # add empty constraint dictionary for duplicate atom
                acd[adup] = {}
                for barevar, con in acd[a].iteritems():
                    formula = con.formula
                    if barevar in ("x", "y", "z"):
                        symidx = "xyz".index(barevar)
                        if ijk[symidx] != 0:
                            formula += " + %i" % ijk[symidx]
                        if mno[symidx] > 1:
                            formula = "(%s)/%.1f" % (formula, mno[symidx])
                            formula = re.sub(r'\((@\d+)\)', r'\1', formula)
                    # keep other formulas intact and add constraint
                    # for barevar of the duplicate atom
                    acd[adup][barevar] = Constraint(formula)
        # replace original atoms with newatoms
        self.initial[:] = newatoms
        for ai, an in zip(self.initial, newatoms):
            if an in acd:
                acd[ai] = acd[an]
        # and rebuild their constraints
        self._restoreAtomConstraints(acd)
        # take care of lattice parameters
        self.initial.lattice.setLatPar(
                a=mno[0]*self.initial.lattice.a,
                b=mno[1]*self.initial.lattice.b,
                c=mno[2]*self.initial.lattice.c )
        # adjust lattice constraints if present
        latvars = ( "lat(1)", "lat(2)", "lat(3)" )
        for var, multiplier in zip(latvars, mno):
            if var in self.constraints and multiplier > 1:
                con = self.constraints[var]
                formula = "%.0f*(%s)" % (multiplier, con.formula)
                formula = re.sub(r'\((@\d+)\)', r'\1', formula)
                con.formula = formula
        return