Uranyl ion in solution

Using fuzzy degeneracy, virtual paths, and arbitrary single scattering paths

In this experiment, the control sample was a buffered solution with a dissolved uranyl acetate. Following the example of S. Kelly, et al., Geochem. Cosmo. Acta, 66:22, (2002) p. 3875, 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:

title = Templeton et al.
title = Redetermination and Absolute configuration of Sodium Uranyl(VI) triacetate.
title = Acta Cryst 1985 C41 1439-1441
space = P 21 3
a     = 10.6890   b    = 10.6890    c    =  10.6890
core  =  U        edge =  L3        rmax =  7.0
atoms
! elem   x          y          z     tag  
  U     0.42940    0.42940    0.42940  U  
  Na    0.82860    0.82860    0.82860  Na 
  O     0.33430    0.33430    0.33430  Oax
  O     0.52420    0.52420    0.52420  Oax
  O     0.38340    0.29450    0.61100  Oeq
  O     0.54640    0.24430    0.50070  Oeq
  C     0.47860    0.22600    0.59500  C
  C     0.50880    0.12400    0.68620  C

This produces a complex crystal structure that includes the uranyl complex onthe right.

uranyl.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. The rest of the crystal is simply unused. I expect that the solvated complex looks basically like this snippet of the full crystal.

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.

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!

In this first code example, FEFF is run using a feff.inp that is already on disk. The potential and path finder calculations are made, at lines 67. Then the first few paths are plotted at lines 8-13.

Line 14 is a feature of the screen UI which is used to pause the script until the user hits return, otherwise the plot made using Gnuplot would blink off the screen. The pause method is imported when the UI is set to screen, as at line 3. Without explicitly setting the screen UI, attempting to call the pause method will do nothing.

  1. #!/usr/bin/perl
  2. use Demeter qw(:plotwith=gnuplot :ui=screen);
  3. my $feff = Demeter::Feff->new(file=>'UAce/feff.inp');
  4. $feff -> set(workspace=>'UAce', screen=>0);
  5. $feff -> potph;
  6. $feff -> pathfinder;
  7. my @list = @{ $feff-> pathlist };
  8. foreach (@list[0..4]) {
  9.   my $this = Demeter::Path->new(parent=>$feff,
  10.     sp=>$_);
  11.   $this-> plot('r');
  12. };
  13. $feff -> pause(-1);
  14. $feff -> freeze('UAce/uace.yaml');

The state of the FEFF calculation is frozen to disk at line 15 and will be used in the following example to import the paths. This is a handy trick that was not used in the previous examples. By freezing the FEFF calculation, you do not have to run FEFF on the fly before your fit. This will save a bit of time and, perhaps, make development of a fitting model a bit less tedious.

Looking at the interpretation of the FEFF calculation is useful before launching into the fit. This can be done with the intrp command line tool (which has some nice output options, including html, TeX, and ASCII-colored screen text) or by using the intrp method in your script.

Note that, in sodium uranyl triacetate, the two dioxo scattering paths differ in path length by about 0.006 Å and the equatorial oxygens are split into two groups of three that differ in distance also by about 0.006 Å. In this case, the fuzzy degeneracy is quite helpful. The fit is probably not so sensitive to such a small difference in scattering length, which may not even exist in the solution sample. Because the degeneracy fuzz is set by default to 0.01 Å, the two dioxo scatterers are made into a single scattering path of degeneracy 2 and the 6 equatorial oxygens are made into a single scattering path of degeneracy 6. This simplifies the bookkeeping aspects of creating the fitting model.

Here is the script that sets up the fitting model.

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

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

  1. At line 4, I turn on the gnuplot plotting backend and enable the screen UI 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 have use the normal interface.

  4. At lines 33-35, I import the results of a Feff calculation that I had previously made using the first DEMETER script on this page.

  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 line 40, I define some Path attributes that will be used in common for all Path objects defined.

  7. 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.

  8. At lines 122-127, I do a bit of trickery to make the spiffy stacked plot shown below. Note that the VPath and SSPath objects get plotted in the same way as the Data and Paths. Indeed, the plot is accomplished by looping over all the objects that I want to display in the plot. Note the use of the data attribute at line 124. The y_offset is an attribute of the Data object. A path-like object gets its value of y_offset from its associated Data object. A Data object is its own data attribute. This little idiom is the way of always setting something like y_offset to what it needs to be to plot the current object in a loop.

  9. The last line pauses the script before terminating so that you have time to examine the plot. The Gnuplot process ends when the script ends, making the plot window go away.

The fit as χ(k). This was made by substituting lines 122-127 with:


      $data -> po -> set(kweight=>2, plot_fit=>1);
      my ($step, $jump) = (0,-0.5);
      map {$_->data->y_offset($step);
           $_->plot('k');
           $step+=$jump;}
        ($data, $paths[0], $paths[1],
         $carbon, $axialms, $ss);
      $data -> plot_window('k');

uranyl_k.png

The fit as the magnitude of χ(R). This was made by substituting lines 122-127 with:


      $data -> po -> set(kweight=>2, plot_fit=>1);
      $data -> po -> set(r_pl => 'm');
      my ($step, $jump) = (0,-0.25);
      map {$_->data->y_offset($step);
           $_->plot('r');
           $step+=$jump;}
        ($data, $paths[0], $paths[1],
         $carbon, $axialms, $ss);
      $data -> plot_window('r');

uranyl_rmag.png

The fit as the real part of χ(R). This is the plot that is made by lines 122-127.

uranyl_rre.png