def main(): # Make the data and the recipe strufile = "data/C60.stru" q = numpy.arange(1, 20, 0.05) makeData(strufile, q, "C60.iq", 1.0, 100.68, 0.005, 0.13, 2) # Make the recipe recipe = makeRecipe(strufile, "C60.iq") # Optimize scipyOptimize(recipe) # Generate and print the FitResults res = FitResults(recipe) # We want to see how much speed-up we get from bringing the scale and # background outside of the intensity generator. Get the number of calls # to the residual function from the FitRecipe, and the number of calls to # 'iofq' from the IntensityGenerator. rescount = recipe.fithooks[0].count calcount = recipe.bucky.I.count footer = "iofq called %i%% of the time"%int(100.0*calcount/rescount) res.printResults(footer = footer) # Plot! plotResults(recipe) return
def main(): """Refine the C60 molecule. From previous examples we know that the radius of the model structure is slightly too small. The annealing algorithm has to determine the proper radius, and must also account for thermal vibrations in the PDF. """ molecule = makeC60() recipe = makeRecipe(molecule, "data/C60.gr") recipe.fithooks[0].verbose = 0 # Optimize # Group related atomic positions for the optimizer parlist = [getXYZNames(i) for i in xrange(60)] groupAnneal(recipe, parlist) # Print results recipe.fix("all") res = FitResults(recipe, showfixed=False) res.printResults() # Save the structure molecule.write("C60_refined.stru", "pdffit") # Plot results plotResults(recipe) return
def main(): """Refine the C60 molecule. From previous examples we know that the radius of the model structure is slightly too small. The annealing algorithm has to determine the proper radius, and must also account for thermal vibrations in the PDF. """ molecule = makeC60() recipe = makeRecipe(molecule, "data/C60.gr") recipe.fithooks[0].verbose = 0 # Optimize # Group related atomic positions for the optimizer parlist = [getXYZNames(i) for i in xrange(60)] groupAnneal(recipe, parlist) # Print results recipe.fix("all") res = FitResults(recipe, showfixed = False) res.printResults() # Save the structure molecule.write("C60_refined.stru", "pdffit") # Plot results plotResults(recipe) return
def regression(self, fit, title): """ Apply least squares to the free parameters. """ print('Fitting PDF of', title, 'to expt. data.') fit.fithooks[0].verbose = 0 # We can now execute the fit using scipy's least square optimizer. # free parameters one-by-one and fit self.plot_fit(fit, title, num=0) print(title, '[1/3]') fit.free("scale") leastsq(fit.residual, fit.values) self.plot_fit(fit, title, num=1) print(title, '[2/3]') fit.free("delta2") leastsq(fit.residual, fit.values) self.plot_fit(fit, title, num=2) print(title, '[3/3]') fit.free("all") leastsq(fit.residual, fit.values) self.plot_fit(fit, title, num=3) print(title, 'Done!') ContributionResult = FitResults(fit) ContributionResult.saveResults(title + '.results') return
def __init__(self, recipe: md.MyRecipe): self._recipe = recipe self._contribution = next(iter(recipe.contributions.values())) self._fit_result = FitResults(self._recipe, update=False) self._verbose: int = 1 self._order: tp.List[tp.Union[str, tp.Iterable[str]]] = [] self._options: dict = {} self._fit_state = None
def main(): """Set up and refine the recipe.""" # Make the data and the recipe cdsciffile = "data/CdS.cif" znsciffile = "data/ZnS.cif" data = "data/CdS_ZnS_nano.gr" # Make the recipe stru1 = loadCrystal(cdsciffile) stru2 = loadCrystal(znsciffile) recipe = makeRecipe(stru1, stru2, data) from diffpy.srfit.fitbase.fithook import PlotFitHook recipe.pushFitHook(PlotFitHook()) recipe.fithooks[0].verbose = 3 # Optimize - we do this in steps to help convergence recipe.fix("all") # Start with the lattice parameters. In makeRecipe, these were tagged with # "lat". Here is how we use that. recipe.free("lat") leastsq(recipe.residual, recipe.values, maxfev = 50) # Now the scale and phase fraction. recipe.free("scale", "scale_CdS") leastsq(recipe.residual, recipe.values, maxfev = 50) # The ADPs. recipe.free("adp") leastsq(recipe.residual, recipe.values, maxfev = 100) # The delta2 parameters. recipe.free("delta2_cds", "delta2_zns") leastsq(recipe.residual, recipe.values, maxfev = 50) # The shape parameters. recipe.free("radius", "thickness") leastsq(recipe.residual, recipe.values, maxfev = 50) # The positional parameters. recipe.free("xyz") leastsq(recipe.residual, recipe.values) # Generate and print the FitResults res = FitResults(recipe) res.printResults() # Plot! plotResults(recipe) return
def main(): """Set up and refine the recipe.""" # Make the data and the recipe cdsciffile = "data/CdS.cif" znsciffile = "data/ZnS.cif" data = "data/CdS_ZnS_nano.gr" # Make the recipe stru1 = CreateCrystalFromCIF(file(cdsciffile)) stru2 = CreateCrystalFromCIF(file(znsciffile)) recipe = makeRecipe(stru1, stru2, data) from diffpy.srfit.fitbase.fithook import PlotFitHook recipe.pushFitHook(PlotFitHook()) recipe.fithooks[0].verbose = 3 # Optimize - we do this in steps to help convergence recipe.fix("all") # Start with the lattice parameters. In makeRecipe, these were tagged with # "lat". Here is how we use that. recipe.free("lat") leastsq(recipe.residual, recipe.values, maxfev=50) # Now the scale and phase fraction. recipe.free("scale", "scale_CdS") leastsq(recipe.residual, recipe.values, maxfev=50) # The ADPs. recipe.free("adp") leastsq(recipe.residual, recipe.values, maxfev=100) # The delta2 parameters. recipe.free("delta2_cds", "delta2_zns") leastsq(recipe.residual, recipe.values, maxfev=50) # The shape parameters. recipe.free("radius", "thickness") leastsq(recipe.residual, recipe.values, maxfev=50) # The positional parameters. recipe.free("xyz") leastsq(recipe.residual, recipe.values) # Generate and print the FitResults res = FitResults(recipe) res.printResults() # Plot! plotResults(recipe) return
def gaussianfit(x, y, dy=None, A=1.0, sig=1.0, x0=0.0): #def gaussianfit(arg): '''main function to fit and plot gaussian curve x, y, dy: gaussian date to be fitted A, sig, x0: initial value ''' #print type(arg) dy = np.ones_like(x) if dy==None else dy recipe = makeRecipe(x, y, dy, A, sig, x0) scipyOptimize(recipe) res = FitResults(recipe) res.printResults() plotResults(recipe) return
def report(recipe: MyRecipe) -> FitResults: """Print out the fitting result. Parameters ---------- recipe : MyRecipe The recipe after refinement. Returns ------- res : FitResults The object contains the fit results. """ res = FitResults(recipe) res.printResults() return res
def main(): # Create the recipe recipe = makeRecipeII() # Refine using the optimizer of your choice scipyOptimize(recipe) # Get the results in a FitResults object. res = FitResults(recipe) # Print the results res.printResults() # Plot the results plotResults(recipe) return
def refine(self): '''Optimize the recipe created above using scipy. ''' from scipy.optimize.minpack import leastsq leastsq(self.recipe.residual, self.recipe.values) self.results = FitResults(self.recipe) print("Fit results:\n") print(self.results) return
def main(): p = Profile() p.loadtxt("data/gaussian.dat") # FitContribution operations # "|=" - Union of necessary components. # "<<" - Inject a parameter value c = FitContribution("g1") c |= p c |= "A * exp(-0.5*(x-x0)**2/sigma**2)" c.A << 0.5 c.x0 << 5 c.sigma << 1 # FitRecipe operations # "|=" - Union of necessary components. # "+=" - Add Parameter or create a new one. Each tuple is a set of # arguments for either setVar or addVar. # "*=" - Constrain a parameter. Think of "*" as a push-pin holding one # parameter's value to that of another. # "%=" - Restrain a parameter or equation. Think of "%" as a rope # loosely tying parameters to a value. r = FitRecipe() r |= c r += (c.A, 0.5), (c.x0, 5), "sig" r *= c.sigma, "sig" r %= c.A, 0.5, 0.5 from gaussianrecipe import scipyOptimize scipyOptimize(r) res = FitResults(r) # Print the results. res.printResults() # Plot the results. from gaussianrecipe import plotResults plotResults(r) return
def main(): """The workflow of creating, running and inspecting a fit.""" # Create the recipe recipe = makeRecipe() # Refine using the optimizer of your choice scipyOptimize(recipe) # Get the results. res = FitResults(recipe) # Print the results res.printResults() # Plot the results plotResults(recipe) return
def main(): p = Profile() p.loadtxt("data/gaussian.dat") # FitContribution operations # "|=" - Union of necessary components. # "<<" - Inject a parameter value c = FitContribution("g1") c |= p c |= "A * exp(-0.5*(x-x0)**2/sigma**2)" c.A << 0.5 c.x0 << 5 c.sigma << 1 # FitRecipe operations # "|=" - Union of necessary components. # "+=" - Add Parameter or create a new one. Each tuple is a set of # arguments for either setVar or addVar. # "*=" - Constrain a parameter. Think of "*" as a push-pin holding one # parameter's value to that of another. # "%=" - Restrain a parameter or equation. Think of "%" as a rope # loosely tying parameters to a value. r = FitRecipe() r |= c r += (c.A, 0.5), (c.x0, 5), 'sig' r *= c.sigma, 'sig' r %= c.A, 0.5, 0.5 from gaussianrecipe import scipyOptimize scipyOptimize(r) res = FitResults(r) # Print the results. res.printResults() # Plot the results. from gaussianrecipe import plotResults plotResults(r) return
def main(): molecule = makeC60() # Make the data and the recipe recipe = makeRecipe(molecule, "data/C60.gr") # Tell the fithook that we want very verbose output. recipe.fithooks[0].verbose = 3 # Optimize from scipy.optimize import leastsq leastsq(recipe.residual, recipe.getValues()) # Print results res = FitResults(recipe) res.printResults() # Plot results plotResults(recipe) return
def main(): # Make two different data sets, each from the same structure, but with # different scale, noise, broadening and background. strufile = "data/C60.stru" q = numpy.arange(1, 20, 0.05) makeData(strufile, q, "C60_1.iq", 8.1, 101.68, 0.008, 0.12, 2, 0.01) makeData(strufile, q, "C60_2.iq", 3.2, 101.68, 0.02, 0.003, 0, 1) # Make the recipe recipe = makeRecipe(strufile, "C60_1.iq", "C60_2.iq") # Optimize # Since the backgrounds have a large effect on the profile, we will refine # them first, but do so separately. # To refine the background from the first contribution, we will fix # all other parameters and give the second contribution no weight in the # fit. recipe.fix("all") recipe.free("bcoeffs1") recipe.setWeight(recipe.bucky2, 0) scipyOptimize(recipe) # Now do the same for the second background recipe.fix("all") recipe.free("bcoeffs1") recipe.setWeight(recipe.bucky2, 1) recipe.setWeight(recipe.bucky1, 0) scipyOptimize(recipe) # Now refine everything with the structure parameters included recipe.free("all") recipe.setWeight(recipe.bucky1, 1) scipyOptimize(recipe) # Generate and print the FitResults res = FitResults(recipe) res.printResults() # Plot! plotResults(recipe) return
def _make_df(recipe: MyRecipe) -> Tuple[pd.DataFrame, FitResults]: """ :param recipe: fit recipe. :return: """ df = pd.DataFrame() res = FitResults(recipe) df["name"] = ["Rw", "half_chi2"] + res.varnames df["val"] = [res.rw, res.chi2 / 2] + res.varvals.tolist() df["std"] = [np.nan, np.nan] + res.varunc df = df.set_index("name") return df, res
def _make_df(recipe: MyRecipe) -> pd.DataFrame: """ get Rw and fitting parameter values from recipe and make them a pandas dataframe :param recipe: fit recipe. :return: """ df = pd.DataFrame() res = recipe.res = FitResults(recipe) df["name"] = ["Rw", "half_chi2"] + res.varnames df["val"] = [res.rw, res.chi2 / 2] + res.varvals.tolist() df["std"] = [0, 0] + res.varunc df = df.set_index("name") return df
def main(): """The workflow of creating, running and inspecting a fit.""" # Start by creating the recipe. The recipe describes the data to be fit, # the profile generator used to simulate the data and the variables that # will be tuned by the optimizer. recipe = makeRecipe() # Refine using the optimizer of your choice. scipyOptimize(recipe) # Get the results in a FitResults object. The FitResults object stores the # current state of the recipe, and uses it to calculate useful statistics # about the fit. res = FitResults(recipe) # Print the results. res.printResults() # Plot the results. plotResults(recipe) return
def save_res(recipe: FitRecipe, base_name: str, folder: str) -> Path: """Save the fitting results. Parameters ---------- recipe : FitRecipe The refined recipe. base_name : str The base name of the result file. The file name will be "{base_name}.res". folder : str The folder to save the fitting result file. Returns ------- res_file : Path The path to the fitting result file. """ res_file = Path(folder) / "{}.res".format(base_name) res = FitResults(recipe) res.saveResults(str(res_file)) return res_file
def main(): # Make the data and the recipe data = "../data/MEF_300-00000.gr" basename = "MEF_300K_LS" print basename # Make the recipe from diffpy.Structure import Structure stru1 = Structure(filename='../data/MEF.cif') stru2 = Structure(filename='../data/MEF.xyz') stru3 = Structure(filename='../data/MEF.xyz') recipe = makeRecipe(stru1, stru2, stru3, data) if _plot: from diffpy.srfit.fitbase.fithook import PlotFitHook recipe.pushFitHook(PlotFitHook()) recipe.fithooks[0].verbose = 3 from scipy.optimize import leastsq leastsq(recipe.residual, recipe.values) # Save structures stru1.write(basename + "_Cryst_B_zoomed.stru", "pdffit") stru2.write(basename + "_Mole_B_zoomed.xyz", "xyz") stru2.write(basename + "_Intra_zoomed.xyz", "xyz") profile = recipe.MEF.profile #import IPython.Shell; IPython.Shell.IPShellEmbed(argv=[])() profile.savetxt(basename + ".fit") # Generate and print the FitResults res = FitResults(recipe) res.printResults() header = "MEF Organic PDF fit.\n" res.saveResults(basename + ".res", header=header) # Plot! if _plot: plotResults(recipe)
def main(): # Make the data and the recipe data = "../data/ON_RT_5-00000.gr" basename = "ROY_Least_Squares" print basename # Make the recipe from diffpy.Structure import Structure stru1 = Structure(filename='../data/QAXMEH_ON.cif') stru2 = Structure(filename='../data/QAXMEH_ON.xyz' ) # this is non-distorted structure, keep it fixed. stru3 = Structure(filename='../data/QAXMEH_ON.xyz') recipe = makeRecipe(stru1, stru2, stru3, data) if _plot: from diffpy.srfit.fitbase.fithook import PlotFitHook recipe.pushFitHook(PlotFitHook()) recipe.fithooks[0].verbose = 3 from scipy.optimize import leastsq leastsq(recipe.residual, recipe.values) # Save structures stru1.write(basename + "_Cryst_B_zoomed.stru", "pdffit") stru2.write(basename + "_Mole_B_zoomed.xyz", "xyz") stru2.write(basename + "_Intra_zoomed.xyz", "xyz") profile = recipe.ROY.profile profile.savetxt(basename + ".fit") res = FitResults(recipe) res.printResults() header = "A+B-C.\n" res.saveResults(basename + ".res", header=header) # Plot! if _plot: plotResults(recipe, basename)
def main(): """ This will run by default when the file is executed using "python file.py" in the command line Parameters ---------- None Returns ---------- None """ # Make some folders to store our output files. resdir = Path("res") fitdir = Path("fit") figdir = Path("fig") folders = [resdir, fitdir, figdir] # Loop over all folders for folder in folders: # If the folder does not exist... if not folder.exists(): # ...then we create it. folder.mkdir() # Let the user know what fit we are running by printing to terminal. basename = FIT_ID print(f"\n{basename}\n") # Establish the full location of the data. data = DPATH / GR_NAME # Establish the location of the cif file with the structure of interest # and load it into a diffpy structure object. strudir = DPATH cif_file = strudir / CIF_NAME # Initialize the Fit Recipe by giving it this diffpy structure # as well as the path to the data file. p_cif = getParser('cif') structure = p_cif.parseFile(str(cif_file)) space_group = p_cif.spacegroup.short_name # Initialize the Fit Recipe by giving it this diffpy structure # as well as the path to the data file. recipe = makerecipe(cif_file, data) # Let's set the calculation range! recipe.crystal.profile.setCalculationRange(xmin=PDF_RMIN, xmax=PDF_RMAX, dx=PDF_RSTEP) # Add, initialize, and tag variables in the Fit Recipe object. # In this case we also add psize, which is the NP size. recipe.addVar(recipe.crystal.s1, SCALE_I, tag="scale") # Set an equation, based on your PDF generators. Here we add an extra layer # of complexity, incorporating "f" int our equation. This new term # incorporates damping to our PDF to model the effect of finite crystallite size. # In this case we use a function which models a spherical NP. from diffpy.srfit.pdf.characteristicfunctions import sphericalCF recipe.crystal.registerFunction(sphericalCF, name="f") recipe.crystal.setEquation("s1*G1*f") recipe.addVar(recipe.crystal.psize, PSIZE_I, tag="psize") # Initialize the instrument parameters, Q_damp and Q_broad, and # assign Q_max and Q_min. # Note, here we do not add the qdamp and qbroad parameters to the fit!!! # They are fixed here, because we refined them in the Ni standard fit! recipe.crystal.G1.qdamp.value = QDAMP_I recipe.crystal.G1.qbroad.value = QBROAD_I recipe.crystal.G1.setQmax(QMAX) recipe.crystal.G1.setQmin(QMIN) # Use the srfit function constrainAsSpaceGroup to constrain # the lattice and ADP parameters according to the Fm-3m space group. from diffpy.srfit.structure import constrainAsSpaceGroup spacegroupparams = constrainAsSpaceGroup(recipe.crystal.G1.phase, space_group) # Add and initialize delta, the lattice parameter, and a thermal parameter, # but not instrumental parameters to Fit Recipe. # The instrumental parameters will remain fixed at values obtained from # the Ni calibrant in our previous example. As we have not added them through # recipe.addVar, they cannot be refined. for par in spacegroupparams.latpars: recipe.addVar(par, value=CUBICLAT_I, name="fcc_Lat", tag="lat") for par in spacegroupparams.adppars: recipe.addVar(par, value=UISO_I, name="fcc_ADP", tag="adp") recipe.addVar(recipe.crystal.G1.delta2, name="Pt_Delta2", value=DELTA2_I, tag="d2") # Tell the Fit Recipe we want to write the maximum amount of # information to the terminal during fitting. recipe.fithooks[0].verbose = 0 refine_params = ["scale", "lat", "psize", "adp", "d2", "all"] recipe.fix("all") for params in refine_params: recipe.free(params) print(f"\n****\nFitting {recipe.getNames()} against " f"{GR_NAME} with {CIF_NAME}\n") least_squares(recipe.residual, recipe.values, x_scale="jac") # We use the savetxt method of the profile to write a text file # containing the measured and fitted PDF to disk. # The file is named based on the basename we created earlier, and # written to the fitdir directory. profile = recipe.crystal.profile profile.savetxt(fitdir / (basename + ".fit")) # We use the FitResults function to parse out the results from # the optimized Fit Recipe. res = FitResults(recipe) # We print these results to the terminal. res.printResults() # We grab the fit Rw rw = res.rw # We use the saveResults method of FitResults to write a text file # containing the fitted parameters and fit quality indices to disk. # The file is named based on the basename we created earlier, and # written to the resdir directory. header = "crystal_HF.\n" res.saveResults(resdir / (basename + ".res"), header=header) # We use the plotresults function we created earlier to make a plot of # the measured, calculated, and difference curves. We show this # as an interactive window and then write a pdf file to disk. # The file is named based on the basename we created earlier, and # written to the figdir directory. plotresults(recipe, figdir / basename) # Let make a dictionary to hold our results. This way make reloading the # fit parameters easier later refined_dict = dict() refined_dict['rw'] = rw.item() # We loop over the variable names, the variable values, and the variable uncertainties (esd) for name, val, unc in zip(res.varnames, res.varvals, res.varunc): # We store the refined value for this variable using the "value" key. # We use the ".item()" method because "res.varvals" exist as # numpy.float64 objects, and we want them as regular python floats. if name not in refined_dict: refined_dict[name] = dict() refined_dict[name]["value"] = val.item() refined_dict[name]["uncert"] = unc.item() with open(basename + ".yml", 'w') as outfile: yaml.safe_dump(refined_dict, outfile)
def default_refine(gr_file: str, res_dir: str, stru_file: str = None) -> Tuple[float, str, str]: """ Refine the G(r) using a default recipe for Ni. Parameters ---------- gr_file Path to the gr file. res_dir Directory to save csv and fgr file. stru_file User defined structure file. If None, the default file will be used: "/Users/sst/project/cal_and_int/Ni.cif" Returns ------- rw Rw value. csv_file Path to fitting parameters csv file. fgr_file Path to fitted data fgr file. """ file_name_base = os.path.splitext(os.path.basename(gr_file))[0] print(f"Refine {file_name_base}, please wait ...") default_stru_file = "/Users/sst/project/Myscripts/tests/Ni.cif" stru_file = stru_file if stru_file else default_stru_file ni = GenConfig("Ni", stru_file, ncpu=4) config_ni = ConConfig(name="one_phase", data_id=0, data_file=gr_file, fit_range=(1., 60., .01), eq="Ni", phases=ni, qparams=(0.04, 0.02)) recipe = make(config_ni) con: FitContribution = recipe.one_phase gen: PDFGenerator = con.Ni recipe.addVar(gen.scale, value=0.4) recipe.addVar(gen.delta2, value=2.0) recipe.addVar(gen.qdamp) recipe.addVar(gen.qbroad) sgpars = constrainAsSpaceGroup(gen.phase, 225) for par in sgpars.latpars: recipe.addVar(par, tag="lat") for par in sgpars.adppars: recipe.addVar(par, tag="adp", value=0.006) recipe.fix("all") recipe.free("scale") fit(recipe, verbose=0) recipe.free("lat") fit(recipe, verbose=0) recipe.free("adp") fit(recipe, verbose=0) recipe.free("delta2") fit(recipe, verbose=0) recipe.free("qdamp", "qbroad") fit(recipe, verbose=0) rw = float(FitResults(recipe).rw) base_name = os.path.join(res_dir, file_name_base) csv_file, fgr_file = old_save(recipe, "one_phase", base_name) plot(recipe) plt.title(f"Ni fitting (Rw = {rw:.3f})") plt.show() return rw, csv_file, fgr_file
pylab.plot(nr,ndiff,'g-',label="G(r) neutron diff") pylab.plot(nr,ndiffzero,'k-') pylab.xlabel("$r (\AA)$") pylab.ylabel("$G (\AA^{-2})$") pylab.legend(loc=1) pylab.show() return if __name__ == "__main__": # Make the data and the recipe ciffile = "data/ni.cif" xdata = "data/ni-q27r60nodg-xray.gr" ndata = "data/ni-q27r100-neutron.gr" # Make the recipe recipe = makeRecipe(ciffile, xdata, ndata) # Optimize scipyOptimize(recipe) # Generate and print the FitResults res = FitResults(recipe) res.printResults() # Plot! plotResults(recipe) # End of file
def main(): """ This will run by default when the file is executed using "python file.py" in the command line Parameters ---------- None Returns ---------- None """ # Make some folders to store our output files. resdir = Path("res") fitdir = Path("fit") figdir = Path("fig") folders = [resdir, fitdir, figdir] # Loop over all folders for folder in folders: # If the folder does not exist... if not folder.exists(): # ...then we create it. folder.mkdir() # Let the user know what fit we are running by printing to terminal. basename = FIT_ID print(f"\n{basename}\n") # Establish the full location of the two datasets. xray_data = DPATH / XRAY_GR_NAME nuetron_data = DPATH / NEUTRON_GR_NAME # Establish the location of the cif file with the structure of interest # and load it into a diffpy structure object. strudir = DPATH cif_file = strudir / CIF_NAME p_cif = getParser('cif') structure = p_cif.parseFile(str(cif_file)) space_group = p_cif.spacegroup.short_name # Initialize the Fit Recipe by giving it this diffpy structure # as well as the path to the data file. # Here we use a new function, which takes both datasets. recipe = makerecipe_coref(cif_file, xray_data, nuetron_data) # We first want to add two scale parameters to our fit recipe, # one for each dataset. recipe.addVar(recipe.xray.s1, XRAY_SCALE_I, tag="scale") recipe.addVar(recipe.neutron.s2, NEUTRON_SCALE_I, tag="scale") # Let's set the calculation range! # Here we use a loop to make it easier to edit both ranges. for cont in recipe._contributions.values(): cont.profile.setCalculationRange(xmin=PDF_RMIN, xmax=PDF_RMAX, dx=PDF_RSTEP) # assign Q_max and Q_min, all part of the PDF Generator object. # It's possible that the PDFParse function we used above # already parsed out ths information, but in case it didn't, we set it # explicitly again here. # We do it for both neutron and PDF configurations recipe.xray.xray_G.setQmax(XRAY_QMAX) recipe.xray.xray_G.setQmin(XRAY_QMIN) recipe.neutron.neutron_G.setQmax(NEUTRON_QMAX) recipe.neutron.neutron_G.setQmin(NEUTRON_QMAX) # Initialize and add the instrument parameters, Q_damp and Q_broad, and # delta and instrumental parameters to Fit Recipe. # We give them unique names, and tag them with our choice of relevant strings. # Again, two datasets means we need to do this for each. recipe.addVar(recipe.xray.xray_G.delta2, name="Ni_Delta2", value=DELTA2_I, tag="d2") recipe.constrain(recipe.neutron.neutron_G.delta2, "Ni_Delta2") recipe.addVar(recipe.xray.xray_G.qdamp, name="xray_Calib_Qdamp", value=XRAY_QDAMP_I, tag="inst") recipe.addVar(recipe.xray.xray_G.qbroad, name="xray_Calib_Qbroad", value=XRAY_QBROAD_I, tag="inst") recipe.addVar(recipe.neutron.neutron_G.qdamp, name="neutron_Calib_Qdamp", value=NEUTRON_QDAMP_I, tag="inst") recipe.addVar(recipe.neutron.neutron_G.qbroad, name="neutron_Calib_Qbroad", value=NEUTRON_QBROAD_I, tag="inst") # Configure some additional fit variables pertaining to symmetry. # We can use the srfit function constrainAsSpaceGroup to constrain # the lattice and ADP parameters according to the Fm-3m space group. # First we establish the relevant parameters, then we cycle through # the parameters and activate and tag them. # We must explicitly set the ADP parameters, because in this case, the # CIF had no ADP data. from diffpy.srfit.structure import constrainAsSpaceGroup # Create the symmetry distinct parameter sets, and constrain them # in the generator. neutron_spacegroupparams = constrainAsSpaceGroup(recipe.neutron.neutron_G.phase, space_group) xray_spacegroupparams = constrainAsSpaceGroup(recipe.xray.xray_G.phase, space_group) # Loop over all the symmetry distinct lattice parameters and add # them to the recipe. # We give them unique names, and tag them with our choice of a relevant string. for xray_par, neutron_par in zip(xray_spacegroupparams.latpars, neutron_spacegroupparams.latpars): recipe.addVar(xray_par, value=CUBICLAT_I, name="fcc_Lat", tag="lat") recipe.constrain(neutron_par, "fcc_Lat") # Loop over all the symmetry distinct ADPs and add # them to the recipe. # We give them unique names, and tag them with our choice of a relevant string. for xray_par, neutron_par in zip(xray_spacegroupparams.adppars, neutron_spacegroupparams.adppars): recipe.addVar(xray_par, value=UISO_I, name="fcc_ADP", tag="adp") recipe.constrain(neutron_par, "fcc_ADP") # Tell the Fit Recipe we want to write the maximum amount of # information to the terminal during fitting. recipe.fithooks[0].verbose = 3 # During the optimization, we fix and free parameters sequentially # as you would in PDFgui. This leads to more stability in the refinement. # We first fix all variables. "all" is a tag which incorporates # every parameter. recipe.fix("all") # Here will will set the weight of each contribution. In this case, we give each equal weight conts = list(recipe._contributions.values()) for cont in conts: recipe.setWeight(cont, 1.0/len(conts)) # We then run a fit using the SciPy function "least_squares" which # takes as its arguments the function to be optimized, here recipe.residual, # as well as initial values for the fitted parameters, provided by # recipe.values. The x_scale="jac" argument is an optional argument # that provides for a bit more stability in the refinement. # "least_squares" is a bit more robust than "leastsq," # which is another optimization function provided by SciPy. # "least_squares" supports bounds on refined parameters, # while "leastsq" does not. refine_params = ["scale", "lat", "adp", "d2", "all"] for params in refine_params: recipe.free(params) print(f"\n****\nFitting {recipe.getNames()} against " f"{XRAY_GR_NAME} and {NEUTRON_GR_NAME} with {CIF_NAME}\n") least_squares(recipe.residual, recipe.values, x_scale="jac") # We use the savetxt method of the profile to write a text file # containing the measured and fitted PDF to disk. # The file is named based on the basename we created earlier, and # written to the fitdir directory. profile = recipe.crystal.profile profile.savetxt(fitdir / (basename + ".fit")) # We use the FitResults function to parse out the results from # the optimized Fit Recipe. res = FitResults(recipe) # We print these results to the terminal. res.printResults() # We grab the fit Rw rw = res.rw # We use the saveResults method of FitResults to write a text file # containing the fitted parameters and fit quality indices to disk. # The file is named based on the basename we created earlier, and # written to the resdir directory. header = "crystal_HF.\n" res.saveResults(resdir / (basename + ".res"), header=header) # We use the plotresults function we created earlier to make a plot of # the measured, calculated, and difference curves. We show this # as an interactive window and then write a pdf file to disk. # The file is named based on the basename we created earlier, and # written to the figdir directory. plotresults(recipe, figdir / basename) # Let make a dictionary to hold our results. This way make reloading the # fit parameters easier later refined_dict = dict() refined_dict['rw'] = rw.item() recipe.free("all") # We loop over the variable names, the variable values, and the variable uncertainties (esd) for name, val, unc in zip(res.varnames, res.varvals, res.varunc): # We store the refined value for this variable using the "value" key. # We use the ".item()" method because "res.varvals" exist as # numpy.float64 objects, and we want them as regular python floats. if name not in refined_dict: refined_dict[name] = dict() refined_dict[name]["value"] = val.item() refined_dict[name]["uncert"] = unc.item() # Finally, let's write our dictionary to a yaml file! with open(basename + ".yml", 'w') as outfile: yaml.safe_dump(refined_dict, outfile)
pylab.plot(r,diffzero,'k-') pylab.xlabel("$r (\AA)$") pylab.ylabel("$G (\AA^{-2})$") pylab.legend(loc=1) pylab.show() return if __name__ == "__main__": # Make the data and the recipe niciffile = "data/ni.cif" siciffile = "data/si.cif" data = "data/si90ni10-q27r60-xray.gr" # Make the recipe recipe = makeRecipe(niciffile, siciffile, data) # Optimize scipyOptimize(recipe) # Generate and print the FitResults res = FitResults(recipe) res.printResults() # Plot! plotResults(recipe) # End of file
# We fix Qdamp based on prior information about our beamline. mnofit.addVar(nucpdf.qdamp, 0.03) # Turn off printout of iteration number. mnofit.clearFitHooks() # Initial structural fit print("Refine PDF using scipy's least-squares optimizer:") print(" variables:", mnofit.names) print(" initial values:", mnofit.values) leastsq(mnofit.residual, mnofit.values) print(" final values:", mnofit.values) print() # Obtain and display the fit results. mnoresults = FitResults(mnofit) print("FIT RESULTS\n") print(mnoresults) # Get the experimental data from the recipe r = mnofit.totpdf.profile.x gobs = mnofit.totpdf.profile.y # Get the calculated PDF and compute the difference between the calculated and # measured PDF gcalc = mnofit.totpdf.evaluate() baseline = 1.1 * gobs.min() gdiff = gobs - gcalc # Plot the structural refinement ax = plt.figure().add_subplot(111)
# The FitRecipe.scalarResidual function returns the sum of squares and can # be used with a minimizer that expects scalar function: from scipy.optimize import fmin fmin(recipe.scalarResidual, [1, 1]) print(recipe.names, "-->", recipe.values) plt.plot(profile.x, profile.y, 'x', profile.x, profile.ycalc, '-') plt.title('Line fit using the fmin scalar optimizer') # <demo> --- stop --- # For a converged fit recipe, the details of the fit can be extracted # with the FitResults class. from diffpy.srfit.fitbase import FitResults res = FitResults(recipe) print(res) # <demo> --- stop --- # Variables defined in the recipe can be fixed to a constant value. recipe.fix(lgx0=51) # The fixed variables can be checked using the "fixednames" and # "fixedvalues" attributes of a recipe. print("free:", recipe.names, "-->", recipe.names) print("fixed:", recipe.fixednames, "-->", recipe.fixedvalues) # The fit can be rerun with a constant variable B. leastsq(recipe.residual, recipe.values)
# We fix Qdamp based on prior information about our beamline. MnOFit.addVar(MnOPDF.qdamp, 0.03, fixed=True) # Turn off printout of iteration number. MnOFit.clearFitHooks() # We can now execute the fit using scipy's least square optimizer. print("Refine PDF using scipy's least-squares optimizer:") print(" variables:", MnOFit.names) print(" initial values:", MnOFit.values) leastsq(MnOFit.residual, MnOFit.values) print(" final values:", MnOFit.values) print() # Obtain and display the fit results. MnOResults = FitResults(MnOFit) print("FIT RESULTS\n") print(MnOResults) # Get the experimental data from the recipe r = MnOFit.MnO.profile.x gobs = MnOFit.MnO.profile.y # Get the calculated PDF and compute the difference between the calculated and # measured PDF gcalc = MnOFit.MnO.evaluate() baseline = 1.1 * gobs.min() gdiff = gobs - gcalc baseline2 = 1.2 * (gdiff + baseline).min() ### NOW DO THE MPDF REFINEMENT USING THE RESIDUAL FROM THE ATOMIC PDF
class ModelBase: """The template for the model class.""" def __init__(self, recipe: md.MyRecipe): self._recipe = recipe self._contribution = next(iter(recipe.contributions.values())) self._fit_result = FitResults(self._recipe, update=False) self._verbose: int = 1 self._order: tp.List[tp.Union[str, tp.Iterable[str]]] = [] self._options: dict = {} self._fit_state = None def parallel(self, ncpu: int) -> None: """Parallel computing. Parameters ---------- ncpu : Number of CPUs. """ fc = self.get_contribution() for g in fc.generators.values(): g.parallel(ncpu) def set_xrange(self, start: float = None, end: float = None, step: float = None) -> None: """Set fitting range. Parameters ---------- start : Start of x. x >= start end : End of x. x <= end step : Step of x. x[i] - x[i-1] == step Returns ------- None """ profile = self.get_profile() profile.setCalculationRange(xmin=start, xmax=end, dx=step) def set_verbose(self, level: int) -> None: """Set verbose level. Parameters ---------- level : The level used. 0 means quiet. Returns ------- None """ self._verbose = level def get_verbose(self) -> int: """Get verbose level Returns ------- Verbose level. """ return self._verbose def set_options(self, **kwargs) -> None: """Set options for fitting. Parameters ---------- kwargs : The options for the scipy.optimize.least_squares. Returns ------- None """ self._options = kwargs def get_options(self) -> dict: """Get options for fitting. Returns ------- A dictionary of options. """ return self._options def set_order(self, *order: tp.Union[str, tp.Iterable[str]]) -> None: """Set the order of fitting parameters. Parameters ---------- order : A list of list or string. Returns ------- None Examples -------- if order is ["A", ["B", "C"]], "A" will be first refined and "B", "C" will be added after and refined. """ order = list(order) self._check_order(order) self._order = order def _check_order(self, order: tp.Any) -> None: """Check the order.""" tags = set(self._recipe._tagmanager.alltags()) if isinstance(order, str): if not hasattr(self._recipe, order) and order not in tags: raise ValueError("'{}' is not in the variable names.".format(order)) elif isinstance(order, tp.Iterable): for x in order: self._check_order(x) else: raise TypeError("'{}' is not allowed.".format(type(order))) def get_order(self) -> tp.List[tp.Union[str, tp.Iterable[str]]]: """Get the order of the parameters Returns ------- A list of parameters. """ return self._order def set_value(self, **kwargs) -> None: """Set the parameter values. Parameters ---------- kwargs : In the format of param = value. Returns ------- None """ self._check_params(kwargs.keys()) for name, value in kwargs.items(): var: Parameter = getattr(self._recipe, name) var.setValue(value) def get_param(self, name: str) -> Parameter: """Get the parameters.""" if not hasattr(self._recipe, name): raise KeyError("No such parameter call '{}' in the recipe.".format(name)) return getattr(self._recipe, name) def set_bound(self, **kwargs) -> None: """Set the bound. Parameters ---------- kwargs : In the form of param = (lb, ub) Returns ------- None """ self._check_params(kwargs.keys()) for name, bound in kwargs.items(): var: Parameter = getattr(self._recipe, name) var.boundRange(*bound) def set_rel_bound(self, **kwargs) -> None: """Set the bound relatively to current value. Parameters ---------- kwargs : In the form of param = (lb, ub) Returns ------- None """ self._check_params(kwargs.keys()) for name, bound in kwargs.items(): var: Parameter = getattr(self._recipe, name) var.boundWindow(*bound) def _check_params(self, params): """Check the parameters.""" for param in params: if not hasattr(self._recipe, param): raise KeyError("There is no parameter called '{}'".format(param)) def _create_recipe(self) -> md.MyRecipe: """Place holder for the method to create the recipe.""" raise NotImplemented def get_contribution(self) -> md.MyContribution: """Get the first contribution in recipe. Returns ------- A FitContribution. """ return self._contribution def get_generators(self) -> tp.Dict[str, tp.Callable]: """Get the generators in a dictionary.""" return self.get_contribution().generators def calc_phase(self, name: str) -> xr.DataArray: """Calculate the data from a generator. Parameters ---------- name : The name of a generator. Returns ------- A xarray.DataArray of calculated y with x as the coordinate. """ gs = self.get_generators() p = self.get_profile() if name not in gs: raise KeyError("There are no generators named '{}'.".format(name)) y = gs[name](p.x) arr = xr.DataArray(y, coords={"x": x}, dims=["x"]) arr["y"].attrs["standard_name"] = "G" arr["y"].attrs["units"] = r"Å$^{-2}$" arr["x"].attrs["standard_name"] = "r" arr["x"].attrs["units"] = "Å" return arr def set_profile(self, profile: Profile) -> None: """Set the data profile. Parameters ---------- profile : A data profile. Returns ------- None """ fc: md.MyContribution = self.get_contribution() fc.setProfile(profile) def get_profile(self) -> Profile: """Get the data profile.""" fc = self.get_contribution() return fc.profile def optimize(self) -> None: """Optimize the model. The scipy.optimize.least_squares is used. Returns ------- None """ if not self._order: raise ValueError("No parameters to refine.") md.optimize(self._recipe, self._order, validate=False, verbose=self._verbose, **self._options) rw = self.get_rw() if self._verbose > 0: print("Optimization result: Rw = {:.6f}.".format(rw)) def get_rw(self) -> float: """Calculate Rw value from profile. ------- Rw value. """ profile = self.get_profile() y, ycalc = profile.y, profile.ycalc return np.sqrt(np.sum((y - ycalc) ** 2) / np.sum(ycalc ** 2)) def update(self) -> None: """Update the result.""" return self._fit_result.update() def show(self) -> None: """Show the values of parameters.""" self._recipe.show() def get_result(self) -> dict: """Get the result in a dictionary""" dct = dict() fr = self._fit_result n = len(fr.varnames) for i in range(n): dct[fr.varnames[i]] = fr.varvals[i] n = len(fr.fixednames) for i in range(n): dct[fr.fixednames[i]] = fr.fixedvals[i] dct["rw"] = self._fit_result.rw return dct def save(self, directory: str, file_prefix: str) -> None: """Save the model parameters. Must update before save. Parameters ---------- directory : The directory to export the files. file_prefix : The prefix of the file name. Returns ------- None """ directory = pathlib.Path(directory) if not directory.is_dir(): directory.mkdir(parents=True) path = directory.joinpath("{}.txt".format(file_prefix)) self._fit_result.saveResults(path) def load(self, filepath: str) -> None: """Load the parameters for the model. Parameters ---------- filepath : The path to the file or the string of the content or a IOstream. Returns ------- None """ initializeRecipe(self._recipe, filepath) def export_result(self) -> xr.Dataset: """Export the result in a dataset.""" dct = self.get_result() ds = xr.Dataset(dct) for name in ds.variables: ds[name].attrs["long_name"] = get_symbol(name) ds[name].attrs["units"] = get_unit(name) ds["rw"].attrs["long_name"] = "$R_w$" return ds def export_fits(self) -> xr.Dataset: """Export the fits in a dataset.""" profile = self.get_profile() ds = xr.Dataset( {"y": (["x"], profile.y), "ycalc": (["x"], profile.ycalc), "yobs": (["xobs"], profile.yobs)}, {"x": (["x"], profile.x), "xobs": (["xobs"], profile.xobs)} ) ds["y"].attrs["standard_name"] = "G" ds["y"].attrs["units"] = r"Å$^{-2}$" ds["ycalc"].attrs["standard_name"] = "G" ds["ycalc"].attrs["units"] = r"Å$^{-2}$" ds["yobs"].attrs["standard_name"] = "G" ds["yobs"].attrs["units"] = r"Å$^{-2}$" ds["x"].attrs["standard_name"] = "r" ds["x"].attrs["units"] = "Å" ds["xobs"].attrs["standard_name"] = "r" ds["xobs"].attrs["units"] = "Å" return ds def save_result(self, directory: str, file_prefix: str) -> None: """Save the fitting result. Parameters ---------- directory : The directory to export the files. file_prefix : The prefix of the file name. Returns ------- None """ directory = pathlib.Path(directory) if not directory.is_dir(): directory.mkdir(parents=True) result = self.export_result() path = directory.joinpath("{}_result.nc".format(file_prefix)) result.to_netcdf(path) def save_fits(self, directory: str, file_prefix: str) -> None: """Save the fitted curves. Parameters ---------- directory : The directory to export the files. file_prefix : The prefix of the file name. Returns ------- None """ directory = pathlib.Path(directory) if not directory.is_dir(): directory.mkdir(parents=True) fits = self.export_fits() path = directory.joinpath("{}_fits.nc".format(file_prefix)) fits.to_netcdf(path) def save_all(self, directory: str, file_prefix: str) -> None: """Save the results, fits and structures in a directory. Parameters ---------- directory : The directory to export the files. file_prefix : The prefix of the file name. Returns ------- None """ self.save(directory, file_prefix) self.save_result(directory, file_prefix) self.save_fits(directory, file_prefix) def plot(self, **kwargs) -> None: """View the fitted curves. Returns ------- None """ fits = self.export_fits() plot_fits(fits, **kwargs)
recipe.addVar(contribution.qdamp, 0.03, fixed=True) recipe.addVar(contribution.nickel.delta2, 5) # Give the recipe away so it can be used! return recipe if __name__ == "__main__": # Make the data and the recipe ciffile = "data/ni.cif" data = "data/ni-q27r100-neutron.gr" # Make the recipe recipe = makeRecipe(ciffile, data) # Optimize scipyOptimize(recipe) # Save the file recipe.nickel.savetxt("nickel_example.fit") # Generate, print and save the FitResults res = FitResults(recipe) res.printResults() res.saveResults("nickel_example.res") # Plot! plotResults(recipe) # End of file
cdseFit.constrain(lattice.b, zoomscale) cdseFit.constrain(lattice.c, zoomscale) # Turn off printout of iteration number. cdseFit.clearFitHooks() # We can now execute the fit using scipy's least square optimizer. print("Refine PDF using scipy's least-squares optimizer:") print(" variables:", cdseFit.names) print(" initial values:", cdseFit.values) leastsq(cdseFit.residual, cdseFit.values) print(" final values:", cdseFit.values) print() # Obtain and display the fit results. cdseResults = FitResults(cdseFit) print("FIT RESULTS\n") print(cdseResults) # Plot the observed and refined PDF. # Get the experimental data from the recipe r = cdseFit.CdSe.profile.x gobs = cdseFit.CdSe.profile.y # Get the calculated PDF and compute the difference between the calculated and # measured PDF gcalc = cdseFit.CdSe.evaluate() baseline = 1.1 * gobs.min() gdiff = gobs - gcalc
# We fix Qdamp based on prior information about our beamline. niFit.addVar(niPDF.qdamp, 0.03, fixed=True) # Turn off printout of iteration number. niFit.clearFitHooks() # We can now execute the fit using scipy's least square optimizer. print("Refine PDF using scipy's least-squares optimizer:") print(" variables:", niFit.names) print(" initial values:", niFit.values) leastsq(niFit.residual, niFit.values) print(" final values:", niFit.values) print() # Obtain and display the fit results. niResults = FitResults(niFit) print("FIT RESULTS\n") print(niResults) # Plot the observed and refined PDF. # Get the experimental data from the recipe r = niFit.nickel.profile.x gobs = niFit.nickel.profile.y # Get the calculated PDF and compute the difference between the calculated and # measured PDF gcalc = niFit.nickel.evaluate() baseline = 1.1 * gobs.min() gdiff = gobs - gcalc
# The FitRecipe.scalarResidual function returns the sum of squares and can # be used with a minimizer that expects scalar function: from scipy.optimize import fmin fmin(rec.scalarResidual, [1, 1]) print(rec.names, "-->", rec.values) plt.plot(linedata.x, linedata.y, 'x', linedata.x, linedata.ycalc, '-') plt.title('Line fit using the fmin scalar optimizer') # <demo> --- stop --- # For a converged fit recipe, the details of the fit can be extracted # with the FitResults class. from diffpy.srfit.fitbase import FitResults res = FitResults(rec) print(res) # <demo> --- stop --- # Variables defined in the recipe can be fixed to a constant value. rec.fix(B=0) # The fixed variables can be checked using the "fixednames" and # "fixedvalues" attributes of a recipe. print("free:", rec.names, "-->", rec.names) print("fixed:", rec.fixednames, "-->", rec.fixedvalues) # The fit can be rerun with a constant variable B. leastsq(rec.residual, rec.values)
recipe.addVar(contribution.scale, 1) recipe.addVar(contribution.qdamp, 0.03, fixed = True) recipe.addVar(contribution.nickel.delta2, 5) # Give the recipe away so it can be used! return recipe if __name__ == "__main__": # Make the data and the recipe ciffile = "data/ni.cif" data = "data/ni-q27r100-neutron.gr" # Make the recipe recipe = makeRecipe(ciffile, data) # Optimize scipyOptimize(recipe) # Save the file recipe.nickel.savetxt("nickel_example.fit") # Generate, print and save the FitResults res = FitResults(recipe) res.printResults() res.saveResults("nickel_example.res") # Plot! plotResults(recipe) # End of file