.(Demeter main page) (Cookbook main page)

A complicated fitting example: A uranyl complex in solution

Background

In a recent experiment, one of the control samples was a buffered solution with a dissolved uranyl acetate. Following the example of S.D. Kelly, et al., X-ray absorption fine-structure determination of pH dependent U-bacterial cell wall interactions, Geochem. Cosmo. Acta, (2002) 66(22) 3875-3891, I will use a crystalline analogue of the solvated moiety as my starting point for the Feff calculation. Here is the input data for Atoms for crystalline sodium uranyl triacetate:

This produces a structure that includes the following uranyl complex:

attachment:uac.png

Following Shelly's example, I will take bits and pieces from this crystalline analog and use them to understand the EXAFS from my solution sample.

Note that there are two crystallographically distinct axial oxygen sites and and two equatorial sites. When you run this through Feff's pathfinder, you find that each site leads to a single scattering path of a slightly different length than its partner. It is highly unlikely that I can use an EXAFS measurement of medium quality to distinguish two slightly different oxygen distances. In Shelly's paper, you can see that she removes these slight degeneracies by using a subset of the paths Feff generates and resetting their degeneracies appropriately. For instance, she uses just one of the axial oxygen paths and multiplies it by 2 to account for the two axial oxygen atoms.

The fit script

This example shows off several aspects of the new technology that Demeter brings to bear on the EXAFS analysis problem, including fuzzy degeneracy, virtual paths, and arbitrary single scattering paths. Read on!

   1 #!/usr/bin/perl
   2 
   3 ## strict and warnings imported automatically with Demeter
   4 use Demeter qw(:plotwith=gnuplot :ui=screen);
   5 
   6 unlink("controlfit.iff") if (-e "controlfit.iff");
   7 
   8 my $prj = Demeter::Data::Prj -> new(file=>'U_DNA.prj');
   9 my $data = $prj -> record(1);
  10 $data -> set_mode(screen  => 0, ifeffit => 1, file => ">controlfit.iff");
  11 
  12 $data -> set(name       => 'U control',
  13              fft_kmin   => 3.0,    fft_kmax  => 10.5,
  14              bft_rmin   => 1,      bft_rmax  => 3.3, #4.22,
  15              fit_space  => 'r',
  16              fit_k1     => 1,      fit_k2    => 1,    fit_k3    => 1,
  17              fit_do_bkg => 0,
  18             );
  19 
  20 my @gds = (
  21            $data->simpleGDS("guess amp   = 1"),
  22            $data->simpleGDS("guess enot  = 0"),
  23            $data->simpleGDS("guess drax  = 0"),
  24            $data->simpleGDS("guess dreq  = 0"),
  25            $data->simpleGDS("guess drc   = 0"),
  26            $data->simpleGDS("guess ssax  = 0.003"),
  27            $data->simpleGDS("guess sseq  = 0.003"),
  28            $data->simpleGDS("guess ssc   = 0.003"),
  29            $data->simpleGDS("guess drhyd = 0"),
  30            $data->simpleGDS("guess sshyd = 0.003"),
  31           );
  32 
  33 my $feff = Demeter::Feff->new(yaml=>"UAce/uace.yaml");
  34 $feff -> set(workspace=>"UAce", screen=>0, buffer=>q{}, save=>1);
  35 my @list_of_paths = @{ $feff->pathlist };
  36 my @paths = ();
  37 my $carbon  = Demeter::VPath->new(name=>"carbon SS + MS");
  38 my $axialms = Demeter::VPath->new(name=>"axial MS");
  39 my $index = 0;
  40 my @common = (parent => $feff, data => $data, s02 => "amp", e0 => "enot");
  41 
  42 ## axial oxygen
  43 my $this_path = Demeter::Path -> new()
  44   -> set(@common, sp => $list_of_paths[$index++],
  45          name   => "axial oxygens",
  46          delr   => "drax",      sigma2 => "ssax",
  47         );
  48 push @paths, $this_path;
  49 
  50 ## equatorial oxygen
  51 $this_path = Demeter::Path -> new()
  52   -> set(@common, sp => $list_of_paths[$index++],
  53          name   => "equatorial oxygens",
  54          delr   => "dreq",      sigma2 => "sseq",
  55         );
  56 push @paths, $this_path;
  57 
  58 ## carbon
  59 $this_path = Demeter::Path -> new()
  60   -> set(@common, sp => $list_of_paths[$index++],
  61          name   => "C",
  62          delr   => "drc",       sigma2 => "ssc",
  63         );
  64 push @paths, $this_path;
  65 $carbon->include($this_path);
  66 
  67 ## C-O triangle
  68 $this_path = Demeter::Path -> new()
  69   -> set(@common, sp => $list_of_paths[$index++],
  70          name   => "C-O triangle",
  71          delr   => "(dreq+drc)/2",   sigma2 => "2*(sseq+ssc)/3",
  72         );
  73 push @paths, $this_path;
  74 $carbon->include($this_path);
  75 
  76 ## axial oxygen rattle MS path
  77 $this_path = Demeter::Path -> new()
  78   -> set(@common, sp => $list_of_paths[$index++],
  79          name   => "axial MS rattle",
  80          delr   => "drax*2",   sigma2 => "ssax*4",
  81         );
  82 push @paths, $this_path;
  83 $axialms->include($this_path);
  84 
  85 ## axial oxygen non-forward scattering MS path
  86 $this_path = Demeter::Path -> new()
  87   -> set(@common, sp => $list_of_paths[$index++],
  88          name   => "axial MS non-forward linear",
  89          delr   => "drax*2",   sigma2 => "ssax*2",
  90         );
  91 push @paths, $this_path;
  92 $axialms->include($this_path);
  93 
  94 ## axial oxygen forward scattering through absorber MS path
  95 $this_path = Demeter::Path -> new()
  96   -> set(@common, sp => $list_of_paths[$index++],
  97          name   => "axial MS forward linear",
  98          delr   => "drax*2",   sigma2 => "ssax*2",
  99         );
 100 push @paths, $this_path;
 101 $axialms->include($this_path);
 102 
 103 
 104 ## make up a scatterer to act as the hydration sphere
 105 my $ss = Demeter::SSPath -> new(@common,
 106                                 name   => "hydration sphere",
 107                                 ipot   => 3,
 108                                 reff   => 3.35,
 109 
 110                                 delr   => 'drhyd',
 111                                 sigma2 => 'sshyd',
 112                                );
 113 push @paths, $ss;
 114 
 115 
 116 my $fit = Demeter::Fit->new(gds   => \@gds,
 117                             data  => [$data],
 118                             paths => \@paths, );
 119 $fit -> fit;
 120 $fit -> logfile("controlfit.log", "U control", q{});
 121 
 122 $data -> po -> set(kweight=>2, rmax=>6, r_pl=>'r', plot_fit=>1);
 123 my ($step, $jump) = (0,-0.3);
 124 map {$_->data->y_offset($step);
 125      $_->plot('r');
 126      $step+=$jump;
 127    } ($data, $paths[0], $paths[1], $carbon, $axialms);
 128 
 129 print "return to continue ";
 130 my $foo = <STDIN>;

Once the fit finishes, the magnitude plot that gets made (on the right is a similar plot, but as the real part):

attachment:fit.png attachment:fit_real.png attachment:fitk.png

Here's the log file for this fit.

There are a lot of interesting things going on in this fit:

  1. At line five, I turn on the gnuplot plotting backend and enable several features that make Demeter more pleasant to use at the command line
  2. At lines 8-18, I import the uranyl data from an Athena project file and set various data and plotting parameters.
  3. In lines 20-31, I define the guess parameters using a bit of syntactic sugar. I could just as well have written Demeter::GDS->new(gds=>'guess', name=>'amp', mathexp=>'1') and so on.

  4. At lines 33-35, I import the results of a Feff calculation that I had previously made using Demeter. More about this below.
  5. At line 37 and 38, I create two VPaths, or virtual paths. These get filled up at lines 65, 74, 83, 92, and 101.
  6. At lines 105-113, I create an arbitrary single scatering path from the oxygen using the SSPath object at a distance where I consider it reasonable to see scattering from the hydration sphere. This SSPath then gets included in the fit like a normal Path.
  7. At lines 122-127, I do a bit of trickery to make the spiffy stacked plot shown above.

Demeter's pathfinder and fuzzy degeneracy

At lines 31-33 in the script, I imported a Feff calculation that I had previously made using the dfeff program that comes with Demeter. After running Atoms using the crystal data above, I ran dfeff like so:

 dfeff --rmax=5 --workspace=UAce --potentials --pathfinder uace.inp

Before examining the Feff calculation, let's take a look at how Feff's pathfinder works. Here is a screenshot from Artemis:

attachment:pathfinder_feff.png

Notice that the slight structural disorder in the sodium uranyl triacetate crystal propagates into the Feff calculation as reduced path degeneracy and a substantial proliferation of paths that are only slightly different. For instance, the two equatorial oxygen paths differ by less than 0.01 Angstrom. This makes it somewhat more complicated to implement a proper fit in Artemis -- either you need to think carefully about degeneracies (which is what I actually did in that Artemis project) or you need to be careful to apply fitting parameters in a properly constrained manner. Either way, it's a lot to think about.

Here is how Demeter's pathfinder improves upon the situation. Demeter introduces the concept of "fuzzy degeneracy". Demeter notices when paths differ in path length by only a small amount (in this case 0.03 Angstroms) and groups them together as degenerate paths. The degeneracy is counted correctly and the value for Reff is the average path length of the fuzzily degenerate paths.

# Templeton et al.
# Redetermination and Absolute configuration of Sodium Uranyl(VI) triacetate.
# The central atom is denoted by this token: <+>
# Cluster size = 5.00000 Angstroms, containing 80 atoms
# 43 paths were found
# Forward scattering cutoff 20.00
# Acta Cryst 1985 C41 1439-1441
# This paths.dat file was written by Demeter 0.2.0
# Distance fuzz = 0.0300 Angstroms
# Angle fuzz = 3.0000 degrees
# Suppressing eta: yes
# -------------------------------------------------------------------------------
#     degen   Reff       scattering path                       I legs   type
 0001   2    1.758  ----  <+> Oax_1  <+>                       2  2 single scattering
 0002   6    2.465  ----  <+> Oeq_1  <+>                       2  2 single scattering
 0003   3    2.852  ----  <+> C_1    <+>                       2  2 single scattering
 0004  12    3.289  ----  <+> Oeq_1  C_1    <+>                0  3 other double scattering
 0005   2    3.516  ----  <+> Oax_1  <+>    Oax_1  <+>         1  4 rattle
 0006   2    3.516  ----  <+> Oax_1  Oax_2  <+>                2  3 non-forward linear
 0007   2    3.516  ----  <+> Oax_1  <+>    Oax_2  <+>         2  4 forward through absorber
 0008   6    3.550  ----  <+> Oeq_1  Oeq_2  <+>                1  3 acute triangle
 0009  24    3.625  ----  <+> Oax_1  Oeq_1  <+>                0  3 other double scattering
 0010   6    3.726  ----  <+> Oeq_1  C_1    Oeq_1  <+>         0  4 dog-leg
 0011   6    3.838  ----  <+> Oeq_1  Oeq_2  <+>                1  3 acute triangle

Instead of having to pick and choose among paths that are only slightly different, I can now simply select the first seven paths in the path list. This makes it much simpler to implement and parameterize the fitting model. In lines 42-101, these 7 paths are imported and parameterized using a very simple fitting model.

Virtual paths

The plot shown above has two noteworthy features (other than the good fit :-? ). The orange and brown traces are plots of "virtual paths". A virtual path, or VPath, is the summation of two or more real paths which can then be plotted as a single object. In this case, I made one VPath out of the various axial multiple scattering paths and another out of the C SS path and the C-O triangle path. This helps keep the plot neat and tidy and serves to explain how a set of paths contributes to the fit.

The VPaths are created and named at lines 37 and 38. I then use the include at lines 65, 74, 83, 92, and 101 to add specific paths to these two VPaths. Finally at line 128 I add these two VPaths to the list of things that I want to plot. Neato!

Arbitrary single scattering paths

I wanted to consider the possibility of scattering from the hydration sphere around the uranyl complex. To do this, I used the oxygen potential from the Feff calculation and placed that oxygen atom at 3.35 Ang. This is shown at lines 105-113 using the SSPath object. This is certainly somewhat ad hoc and could easily be used as a non-physical fudge factor to tidy up a fit. In this case, it seems reasonable that the hydration sphere could be visible in the EXAFS and the distance of ~3.35 seems reasonable given that the water molecule would have to be around twice the axial oxygen distance away from the U atom.

The way I see an SSPath being used is in a situation where you feel you have a physically sensible set of potentials from your Feff calculation, but the structure you are using as the basis of your fit doesn't have an atom at a distance that you supect might be represented in your data. The way this situation is commonly handled in Artemis is to import another structure that does have that atom near that position and then to import just that one path into the fit. The SSPath approach in conceptually similar and considerably simpler.


Demeter/Cookbook/06Uranyl (last edited 2009-10-09 19:46:32 by localhost)