Fitting EXAFS data

Here is a complete fitting example. In this example, data on a copper foil are fit using a model considering isotropic expansion and a correlated Debye model.

Everything up to line 44 should be familiar to you if you have read all the previous parts of this programming guide. An ATHENA project file is imported at line 5. A record from that project file is imported into a Data obejct at line 8 and various parameters of the Data object are set at lines 9-15.

A FEFF calculation is made at lines 17-19. Note the use of chained method calls at line 19. This is possible because the potph method returns the calling object. (So does pathfinder, for that matter, although its return value is thrown away here.) The path list is dereferenced for convenience at line 20.

Various guess and set parameters are defined at lines 23-28 and stored in an array. The parameters will be used to set up a simple fitting model consisting of an amplitude term, an E₀ shift, an isotropic expnasion model for ΔR for each path, and a correlated Debye model for the σ² for each path.

At lines 33-42, various Path objects are defined using the ScatteringPath objects from the FEFF calculation. The path parameters are assigned mathexpressions using the appropriate GDS parameters.

  1. #!/usr/bin/perl
  2. use Demeter qw(:ui=screen);
  3. ## import an Athena project file with copper metal in it
  4. my $prj = Demeter::Data::Prj->new(file='cu_data.prj');
  5. ## make a Data object and set the FT and fit parameters
  6. my $data = $prj->record(1);
  7. $data ->set(name       => 'My copper data',
  8.             fft_kmin   => 3,        fft_kmax   => 14,
  9.             fit_k1     => 1,        fit_k3     => 1,
  10.             bft_rmin   => 1.6,      bft_rmax   => 4.3,
  11.             fit_do_bkg => 0,
  12.            );
  13. ## run a Feff calculation on copper metal
  14. my $feff = Demeter::Feff -> new(file => "cu_metal.inp");
  15. $feff -> set(workspace => "cu_workspace/", screen => 0,);
  16. $feff -> potph -> pathfinder;
  17. my @list_of_paths = $feff-> list_of_paths;
  18. ## make GDS objects for an isotropic expansion, correlated Debye model fit to copper
  19. my @gds =  (Demeter::GDS -> new(gds => 'guess', name => 'alpha', mathexp => 0),
  20.             Demeter::GDS -> new(gds => 'guess', name => 'amp',   mathexp => 1),
  21.             Demeter::GDS -> new(gds => 'guess', name => 'enot',  mathexp => 0),
  22.             Demeter::GDS -> new(gds => 'guess', name => 'theta', mathexp => 500),
  23.             Demeter::GDS -> new(gds => 'set',   name => 'temp',  mathexp => 300),
  24.             Demeter::GDS -> new(gds => 'set',   name => 'sigmm', mathexp => 0.00052),
  25.            );
  26. ## make Path objects for the first 5 paths in copper (3 shell fit)
  27. my @paths = ();
  28. foreach my $i (0 .. 4) {
  29.   $paths[$i] = Demeter::Path -> new();
  30.   $paths[$i]->set(data     => $data,
  31.                   sp       => $list_of_paths[$i];
  32.                   s02      => 'amp',
  33.                   e0       => 'enot',
  34.                   delr     => 'alpha*reff',
  35.                   sigma2   => 'debye(temp, theta) + sigmm',
  36.                  );
  37. };
  38. ## make a Fit object
  39. my $fit = Demeter::Fit -> new(gds   => \@gds,
  40.                               data  => [$data],
  41.                               paths => \@paths
  42.                              );
  43. ## do the fit
  44. $fit -> fit;
  45. ## set up some plotting parameters
  46. $data->po->set(plot_data => 1,   plot_fit  => 1,
  47.                plot_bkg  => 0,   plot_res  => 0,
  48.                plot_win  => 1,   plot_run  => 1,
  49.                kweight   => 2,
  50.                r_pl      => 'r',
  51.               );
  52. $data->plot('r');
  53. $data->pause;

As I said, everything up to this point has been covered already. The fitting magic happens at lines 46-49. A Fit object is defined as a collection of GDS, Data, and Path objects. Those three attributes of the Fit object each takes an anonymous array (as at line 47) or references to named arrays (as at lines 46 and 48). That's it! That's how you make a fit.

Although the first 42 lines of code do not constitute a substantial savings of effort compared to a writing FEFFIT input file or an IFEFFIT script in terms of the amount of typing that you have to do. That changes substantially once the fit is defined. When the fit is requested at line 52, DEMETER does a lot of work disentangle the contents of the arrays containing the GDS, Data, and Path objects. As discussed in an upcoming section extensive checks are run to confirm that all aspects of the fitting model make sense and that there are no obvious errors in the fitting model (e.g. guess parameters that are defined but not used).

Lines 54-58 in the script above defined how the plot at line 61 will appear. Various flags of the Plot object are set such that the data and fit are plotted alonmg with a window showing the fitting range and the running R-factor, which is a way of visualizing how the misfit is distributed over the fitting range. A k-weight of 2 is used to make the Fourier transform before the plot.

cufit.png

In the next section, we'll see how to do multiple data set fitting and how to incorporate more than one FEFF calculation in a fit. In later sections we will explore other features of the Fit object, including its extensive sanity checking, all the interesting things that can be done with the Fit object after a fit finishes, and a discussion of DEMETER's happiness parameter.