Source code for diffpy.pdfgui.control.pdfguimacros

#!/usr/bin/env python
##############################################################################
#
# PDFgui            by DANSE Diffraction group
#                   Simon J. L. Billinge
#                   (c) 2006 trustees of the Michigan State University.
#                   All rights reserved.
#
# File coded by:    Chris Farrow
#
# See AUTHORS.txt for a list of people who contributed.
# See LICENSE.txt for license information.
#
##############################################################################
"""Methods for macros used in pdfgui."""

from __future__ import print_function

import copy
import os

from diffpy.pdfgui.control.controlerrors import ControlValueError
from diffpy.pdfgui.control.fitdataset import FitDataSet


[docs] def makeRSeries( control, fit, maxfirst=None, maxlast=None, maxstep=None, minfirst=None, minlast=None, minstep=None, ): """Make an series of fits with an increasing r-range. The new fits are appended to the end of any current fits in the control. control -- The control object that will contain the fits fit -- The prototype fit maxfirst -- The first value of the maximum of the fit range maxlast -- The last value of the maximum of the fit range maxstep -- The step size of the maximum of the fit range minfirst -- The first value of the minimum of the fit range minlast -- The last value of the minimum of the fit range minstep -- The step size of the minimum of the fit range returns a list of the new fit organization objects """ # Check to see if the input values are correct. # MIN-MIN: FIRST < LAST if minfirst is not None and minlast is not None and not minfirst < minlast: message = ( "The first value of the minimum (%.2f)\ \nmust be less than the last value of the\ \nminimum (%.2f)" % (minfirst, minlast) ) raise ControlValueError(message) # MAX-MAX: FIRST < LAST if maxfirst is not None and maxlast is not None and not maxfirst < maxlast: message = ( "The first value of the maximum (%.2f)\ \nmust be less than the last value of the\ \nmaximum (%.2f)" % (maxfirst, maxlast) ) raise ControlValueError(message) # MAX > MIN: FIRST-FIRST if maxfirst is not None and minfirst is not None and not maxfirst > minfirst: message = ( "The first value of the fit maximum (%.2f)\ \nmust be greater than first value of the fit\ \nminimum (%.2f)." % (maxfirst, minfirst) ) raise ControlValueError(message) # MAX > MIN: LAST-LAST if maxlast is not None and minlast is not None and not maxlast > minlast: message = ( "The last value of the fit maximum (%.2f)\ \nmust be greater than last value of the fit\ \nminimum (%.2f)." % (maxlast, minlast) ) raise ControlValueError(message) # STEP > 0 message = "Step size (%.2f) must be greater than 0." if maxstep is not None and not maxstep > 0: raise ControlValueError(message % maxstep) if minstep is not None and not minstep > 0: raise ControlValueError(message % minstep) # Check to see that either max or min is fully specified maxlist = [maxfirst, maxlast] minlist = [minfirst, minlast] if maxlist.count(None) == 1 or minlist.count(None) == 1: raise ControlValueError("First and last values are partially specified") if maxstep is None and minstep is None: raise ControlValueError("Either minstep or maxstep must be specified.") maxlist = [] minlist = [] if maxfirst is not None: if maxstep is None: maxstep = minstep maxrange = int((maxlast - maxfirst) / (1.0 * maxstep) + 1) maxlist = [maxfirst + i * maxstep for i in range(maxrange)] if minfirst is not None: if minstep is None: minstep = maxstep minrange = int((minlast - minfirst) / (1.0 * minstep) + 1) minlist = [minfirst + i * minstep for i in range(minrange)] # Resize the lists to the length of the shortest serieslen = min(len(maxlist), len(minlist)) if serieslen != 0: maxlist = maxlist[:serieslen] minlist = minlist[:serieslen] else: serieslen = max(len(maxlist), len(minlist)) basename = fit.name fits = [] newname = "" lastname = "" fitcopy = control.copy(fit) # Duplicate the original fit and change the appropriate parameters. for i in range(serieslen): lastname = newname # Loop over datasets for ds in fitcopy.datasets: if minlist: fitrmin = minlist[i] else: fitrmin = ds.fitrmin if maxlist: fitrmax = maxlist[i] else: fitrmax = ds.fitrmax # Check to see that the values are in bounds and sensical if fitrmin < ds.rmin or fitrmin >= ds.rmax: message = ( "Fit minimum (%.2f) is outside the data range\ \n[%.2f, %.2f].\ \nAdjust the range of the series." % (fitrmin, ds.rmin, ds.rmax) ) raise ControlValueError(message) if fitrmax <= ds.rmin or fitrmax > ds.rmax: message = ( "Fit maximum (%.2f) is outside the data range\ \n[%.2f, %.2f].\ \nAdjust the range of the series." % (fitrmax, ds.rmin, ds.rmax) ) raise ControlValueError(message) if fitrmin >= fitrmax: message = ( "Fit minimum (%.2f) is greater than the\ \nmaximum (%.2f).\ \nIncrease maxstep or reduce minstep." % (fitrmin, fitrmax) ) raise ControlValueError(message) # Set the values if all is well if minlist: ds.fitrmin = fitrmin if maxlist: ds.fitrmax = fitrmax # Set the parameters to the previous fit's name, if one exists. if lastname: parval = "=%s" % lastname for par in fitcopy.parameters.values(): par.setInitial(parval) # Now paste the copy into the control. newname = "%s-(%.2f,%.2f)" % (basename, fitrmin, fitrmax) o = control.paste(fitcopy, new_name=newname) fits.append(o) return [f.organization() for f in fits]
# Temperature Series
[docs] 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]
# Doping Series
[docs] 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]
if __name__ == "__main__": from diffpy.pdfgui.control.pdfguicontrol import PDFGuiControl control = PDFGuiControl() control.load("../../tests/testdata/ni.ddp") fit = control.fits[0] olist = makeRSeries(control, fit, 5, 20, 5) print("\n".join(f[0].name for f in olist)) # End of file