IFEFFIT's main job is to help you analyze XAFS data, and IFEFFIT
tries to make it easy to do simple XAFS analysis tasks. These simple tasks
include such things as converting beamline data in
(E), doing pre-edge
subtraction, determining E0, fitting a post-edge (spline) subtraction to
determine
(E) and
(k), and doing XAFS Fourier transforms to look at
data in R-space. These standard tasks of XAFS data manipulation are
described in this section.
The general algebraic manipulation of array data within IFEFFIT makes
the conversion of raw beamline data to XAFS
(E) very easy, and also
allows the averaging and re-scaling (if necessary) of data. For example,
reading raw beamline transmission and fluorescence data and converting this
to
(E) might look like this:
Ifeffit> read_data(file = cu_expt.dat, group= 'cu',
label = 'energy i0 i1 if')
Ifeffit> set cu.xmu_t = log(cu.i1 / cu.i0)
Ifeffit> set cu.xmu_f = (cu.if / cu.i0)
If you've collected data in fluorescence using a multi-element-detector,
you may need to sum several arrays before dividing by I0, something
like this will do the trick:
Ifeffit> read_data(file= med_fluor.dat, group='med', type='raw')
Ifeffit> set med.if= (med.4 + med.5 + med.6 + med.7 + med.8 +
med.9 +med.10 +med.11 +med.12 +med.13 )
Ifeffit> set med.xmu= (med.if / med.2)
Pre-edge subtraction removes the baseline from the EXAFS
(E),
determines the edge energy (used as the origin of k), and
normalizes the above-edge
(E) to 1. All of this is done by
the pre_edge() command which takes arrays of energy and absorption
(E):
Ifeffit> pre_edge(cu.energy, cu.xmu)Like most IFEFFIT command, this simple-looking command actually causes a fair amount of data processing behind the scenes. It also creates several program variables, especially arrays and scalars detailing the pre-edge subtraction. Here's a list of the most important program variables that pre_edge() sets:
| Variable | Description |
| e0 | Energy Origin (found near maximum derivative
of |
| edge_step | Edge Step / normalization constant |
| pre_slope | slope of pre-edge line |
| pre_offset | offset of pre-edge line |
| $group.pre | array of pre-edge subtracted |
| $group.norm | array of pre-edge subtracted and normalized |
where $group is the 'group name' taken from the input
There are several optional arguments to pre_edge() not mentioned here. These arguments can be use to set the ranges over which to fit the pre-edge line and post-edge curve, whether or not to perform every part of the calculation, and so forth. These optional arguments would normally be used like this:
Ifeffit> pre_edge(cu.energy, cu.xmu, e0=8980.5)which would force the value of E0 to be 8980.5 and prevent pre_edge() from trying to determine E0 by itself. As for all commands, the complete set of the optional parameters for pre_edge() are given in the Reference Guide.
Post-edge background subtraction involves drawing a ``smooth background''
(
(E) through the oscillatory part of the XAFS and extracting
(k) using this and the formula
The implementation of AUTOBK in IFEFFIT is encompassed in the
spline() command, which takes arrays of energy and
, and several
optional parameters, and writes out
(k),
(E), and several
scalars. A basic use of spline() would look like this
Ifeffit> spline(cu.energy, cu.xmu, rbkg=1.0)Like pre_edge(), this simple-looking command does quite a bit of data processing behind the scenes, and creates or writes several variables with IFEFFIT. For one thing, spline() will execute pre_edge() unless it's obvious that it shouldn't3. That means that all the output parameters of pre_edge() will be created (or overwritten) by spline(), and E0 and the edge jump
To say much more about the spline() command, I'd have to discuss the
details of the AUTOBK algorithm. I'll try to be brief: spline()
chooses a smooth background spline such that the low-R portion of the
resulting EXAFS
are minimized. That means that spline()
needs to do a Fourier transform of the
it generates. Because of
this, the spline() function takes many command arguments that
resemble Fourier transform parameters, so that in addition to the
arguments of pre_edge() the important arguments that
spline() takes are:
| Variable | Description |
| rbkg | Rbkg, the highest R value to consider background |
| kmin | kmin, the starting k for the Fourier transform |
| kweight | w, the k-weight factor for the Fourier transform. |
Please note that the Fourier transforms appropriate for spline() are not the same as those appropriate for structural analysis. Usually, values of kmin
Like pre_edge(), the spline() command also creates new arrays, most importantly:
| Variable | Description |
| $group.bkg |
|
| $group.k | k, the array of wavenumbers |
| $group.chi | |
Fourier transforms are an integral part of XAFS analysis, and the ability
to perform them quickly and easily is very important. IFEFFIT has
different commands for forward (
k
R) and reverse forward
(
R
q: I'll use q to refer to back-transformed k-space
data) Fourier transforms. They're very similar to one another, so I'll
first discuss forward Fourier transforms with fftf() in
detail, then reverse Fourier transforms with fftr() more
quickly.
IFEFFIT use a simple Fast Fourier transform (FFT), which places some demands on the data transformed by these commands. In addition, XAFS analysis usually imposes some conventions on Fourier transforms, so that the commands discussed here are not general purpose Fourier transform functions, but are really XAFS Fourier transforms. The most obvious difference between 'normal' and 'XAFS' Fourier transforms is that the latter transforms k to R, while a 'normal' FT would transform k to 2R. Aside from a scale factor, this changes the normalization constants used. In addition, the XAFS Fourier transform (at least as IFEFFIT implements it) allows a variety of smoothing window functions, and a weighting factor. More details can be found in XAFS Analysis with IFEFFIT .
The Forward XAFS Fourier transform command is fftf(). It's primary
input is an array of
(k) data. The requirements of the FFT mean that
the input
(k) data must be an array that is on an even k-grid
with grid spacing of
k = 0.05 Å-1, and starting at k = 0.
These are stringent requirements. Fortunately, the spline() command
of section 5.3 writes its output
(k) array
according to these rules. If you're importing
(k) data written from
another program, you'll have to make sure the data is moved to this
k-grid. There are two ways to this: either interpolate the data yourself
(see the interpolation functions in the Reference Guide) or specify the
k-array corresponding to your
data in the fftf() command
and let it do the interpolation for you.
Properly aligned
(k) data can be transformed to R-space using a
command like this:
Ifeffit> fftf(cu.chi, kmin=2.0, kmax=17.0, dk=1.0, kweight=2)while data not on the expected grid would be Fourier transformed like this:
Ifeffit> fftf(cu.chi, k=cu.k,
kmin=2.0, kmax=17.0, dk=1.0, kweight=2)
The keywords kmin, kmax, and dk help define the window
function, and kweight sets the k-weighting factor. You can also
specify the form of the window function. The full list of window types and
their functional form is given in the Reference Guide, but the most useful
ones are the Hanning window ( kwindow=hanning - the default
window function) which ramps up to one on either end of the k-range as
cos2, and the Kaiser-Bessel window (kwindow=kaiser), which is
often thought to give superior peak resolution.
Because it is often necessary to do many Fourier transforms with exactly the same parameters, the fftf() command, can also read Fourier transform parameters from appropriately named program variables. This is actually a feature of many commands, but it seems most useful for the Fourier transform commands. It works like this: setting scalars kmin, kmax, and so forth will have the same effect as setting those arguments to the fftf() command.
Ifeffit> kmin=2.0, kmax=17.0, dk=1.0, kweight=2 Ifeffit> fftf(cu.chi)would have the result as
Ifeffit> fftf(cu.chi, kmin=2.0, kmax=17.0, dk=1.0, kweight=2)In fact, the fftf() command will first read the value for the parameter kmin from the program variable kmin (and so on for the other Fourier transform parameters), and then from the command argument kmin, so that the command argument will always override the program variable value. Furthermore, fftf() will itself set the value of the program variable kmin, possibly overwriting any value you had previously set. Thus
Ifeffit> kmin=2.0, kmax=17.0, dk=1.0, kweight=2 Ifeffit> fftf(cu.chi, kmin=3.) Ifeffit> fftf(other.chi)will use the same Fourier transform parameters (that is kmin = 3Å-1) for both transforms.
A Fourier transform inherently deals with complex data, and a minor
complication arises from the inconvenience that the measured XAFS
(k)
is strictly a real function. In fftf() then, there is an ambiguity
of whether to use the measured XAFS as the real or imaginary part of the
complex XAFS function. Since the measured XAFS is typically described as
the imaginary part of a complex fine-structure function
, one
might be tempted to say that the data
(k) ought to be set to the
imaginary part of
. IFEFFIT usually assumes that the data
(k) is the real part of the complex
and sets the imaginary
part to zero - this is in keeping with the convention of older programs
from the University of Washington, but it is purely a matter of convention.
You can explicitly specify the behavior by saying fftf(imag =
data.chi,...) or fftf(real = data.chi,...). If you
don't specify, the data will be taken as the real part. This is almost
never important - at least not until you want to compare unfiltered
(k) with filtered
(k). The output arrays generated by the
fftf() command are
| Variable | Description |
| $group.win | The k-space window function used |
| $group.r | R, the array of distances |
| $group.chir_mag | | |
| $group.chir_pha | the phase of |
| $group.chir_re |
Re[ |
| $group.chir_im |
Im[ |
It is possible to do ``phase-corrected'' Fourier transforms with IFEFFIT using the theoretical phases from FEFF calculations, but that is beyond the scope of this tutorial.
fftr() is the Reverse XAFS Fourier transform command, mainly used to
filter
(R) data to backtransformed
(k). Following the
convention of FEFFIT, backtransformed k-space is called q to avoid
confusion with the original k-space data. The fftr() command then
transforms
(R) to
(q). As with the forward Fourier transform,
the data is requirements of the FFT mean that the input
(k) data
must be given as an array that is evenly spaced in R with a
fixed grid spacing of
R =
/1024
0.03068 Å, and
starting at R = 0. To further complicate matters, there is
ambiguity as to whether to transform just the real part, just the imaginary
part of
(R), or both. In general, both parts are transformed to
ensure that the overall amplitude scale is preserved.
fftr() has an almost identical command set to fftf(), with k replaced by r in the parameter names. That is, the FT window parameters are defined with rmin, rmax, dr, and so on. The window functional form is set by rwindow. A typical use might look like this
Ifeffit> fftr(real=cu.chir_re, imag=cu.chir_im,
rmin=1.70, rmax=3.0, dk=0.1)
The output arrays generated by the fftf() command are
| Variable | Description |
| $group.rwin | The R-space window function used |
| $group.q | q, the array of wavenumbers |
| $group.chiq_mag | | |
| $group.chiq_pha | the phase of |
| $group.chiq_re |
Re[ |
| $group.chiq_im |
Im[ |
Before ending this section, let me say a few words about ``Fourier filtering''. IFEFFIT's approach to Fourier transforms is fairly general, and the use of two Fourier transforms to ``isolate a shell'' is not a trivial process with IFEFFIT. This partly reflects my experience and belief that ``Fourier filtering'' can not be made a trivial process.
Comparing filtered with unfiltered data is a common desire. Given the ambiguities in how to handle the real and imaginary parts of the data, getting the details right for this are not trivial. Though macros won't be discussed until section 9, using a macro to get the details right is a good idea. Such a macro for Fourier filtering might look like this:
macro filter group "kweight=2,kmin=3" "dr=0"
fftf(real=$1.chi, $2)
fftr(real=$1.chir_re, imag=$1.chir_im, $3)
set $1.chik = $1.chi * $1.k^kweight
set $1.chik_w = $1.chik * $1.win
set $1.chiq = $1.chiq_re / ($1.k^kweight)
end macro
With this macro definition (which takes arguments as group name, k
parameters, and R parameters), you could perform a filter, and overplot
filtered and unfiltered data with
filter data "kweight=2,kmin=3,kmax=15,dk=1" "rmin=1.6,rmax=3." newplot(data.q, data.chiq_re ) plot( data.k, data.chik_w )Note that the convention used here is that the data