Examples

In the following a few code-snippets are shown which should help you getting started with xrayutilities. Not all of the codes shown in the following will be run-able as stand-alone script. For fully running scripts look in the examples directory in the download found here.

Reading data from data files

The io submodule provides classes for reading x-ray diffraction data in various formats. In the following few examples are given.

Reading SPEC files

Working with spec files in xrayutilities can be done in two distinct ways.
  1. parsing the spec file for scan headers; and parsing the data only when needed
  2. parsing the spec file for scan headers; parsing all data and dump them to an HDF5 file; reading the data from the HDF5 file.

Both methods have their pros and cons. For example when you parse the spec-files over a network connection you need to re-read the data again over the network if using method 1) whereas you can dump them to a local file with method 2). But you will parse data of the complete file while dumping it to the HDF5 file.

Both methods work incremental, so they do not start at the beginning of the file when you reread it, but start from the last position they were reading and work with files including data from linear detectors.

An working example for both methods is given in the following.:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import xrayutilities as xu
import os

# open spec file or use open SPECfile instance
try: s
except NameError:
    s = xu.io.SPECFile("sample_name.spec", path="./specdir")

# method (1)
s.scan10.ReadData()
scan10data = s.scan10.data

# method (2)
h5file = os.path.join("h5dir", "h5file.h5")
s.Save2HDF5(h5file) # save content of SPEC file to HDF5 file
# read data from HDF5 file
[angle1, angle2], scan10data = xu.io.geth5_scan(h5file, [10],
                                                "motorname1",
                                                "motorname2")

See also

the fully working example hello world

In the following it is shown how to re-parsing the SPEC file for new scans and reread the scans (1) or update the HDF5 file(2)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
s.Update() # reparse for new scans in open SPECFile instance

# reread data method (1)
s.scan10.ReadData()
scan10data = s.scan10.data

# reread data method (2)
s.Save2HDF5(h5) # save content of SPEC file to HDF5 file
# read data from HDF5 file
[angle1, angle2], scan10data = xu.io.geth5_scan(h5file, [10],
                                                "motorname1",
                                                "motorname2")

Reading EDF files

EDF files are mostly used to store CCD frames at ESRF recorded from various different detectors. This format is therefore used in combination with SPEC files. In an example the EDFFile class is used to parse the data from EDF files and store them to an HDF5 file. HDF5 if perfectly suited because it can handle large amount of data and compression.:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import xrayutilities as xu
import numpy

specfile = "specfile.spec"
h5file = "h5file.h5"

s = xu.io.SPECFile(specfile)
s.Save2HDF5(h5file) # save to hdf5 file

# read ccd frames from EDF files
for i in range(1, 1001, 1):
    efile = "edfdir/sample_%04d.edf" % i
    e = xu.io.edf.EDFFile(efile)
    e.ReadData()
    e.Save2HDF5(h5file, group="/frelon_%04d" % i)

See also

the fully working example provided in the examples directory perfectly suited for reading data from beamline ID01

Other formats

Other formats which can be read include

  • files recorded from Panalytical diffractometers in the .xrdml format.
  • files produces by the experimental control software at Hasylab/Desy (spectra).
  • ccd images in the tiff file format produced by RoperScientific CCD cameras and Perkin Elmer detectors.
  • files from recorded by Seifert diffractometer control software (.nja)
  • basic support is also provided for reading of cif files from structure database to extract unit cell parameters

See the examples directory for more information and working example scripts.

Angle calculation using experiment and material classes

Methods for high angle x-ray diffraction experiments. Mostly for experiments performed in coplanar scattering geometry. An example will be given for the calculation of the position of Bragg reflections.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import xrayutilities as xu
Si = xu.materials.Si  # load material from materials submodule

# initialize experimental class with directions from experiment
hxrd = xu.HXRD(Si.Q(1, 1, -2), Si.Q(1, 1, 1))
# calculate angles of Bragg reflections and print them to the screen
om, chi, phi, tt = hxrd.Q2Ang(Si.Q(1, 1, 1))
print("Si (111)")
print("om,tt: %8.3f %8.3f" % (om, tt))
om, chi, phi, tt = hxrd.Q2Ang(Si.Q(2, 2, 4))
print("Si (224)")
print("om,tt: %8.3f %8.3f" % (om, tt))

Note that on line 5 the HXRD class is initialized without specifying the energy used in the experiment. It will use the default energy stored in the configuration file, which defaults to CuK-alpha1.

One could also call:

hxrd = xu.HXRD(Si.Q(1, 1, -2), Si.Q(1, 1, 1), en=10000) # energy in eV

to specify the energy explicitly. The HXRD class by default describes a four-circle goniometer as described in more detail here.

Similar functions exist for other experimental geometries. For grazing incidence diffraction one might use:

gid = xu.GID(Si.Q(1, -1, 0), Si.Q(0, 0, 1))
# calculate angles and print them to the screen
(alphai, azimuth, tt, beta) = gid.Q2Ang(Si.Q(2, -2, 0))
print("azimuth,tt: %8.3f %8.3f" % (azimuth, tt))

There is on implementation of a GID 2S+2D diffractometer. Be sure to check if the order of the detector circles fits your goniometer, otherwise define one yourself!

There exists also a powder diffraction class, which is able to convert powder scans from angular to reciprocal space.

1
2
3
4
5
6
7
8
9
import xrayutilities as xu
import numpy

energy = 'CuKa12'

# creating powder experiment
xup = xu.PowderExperiment(en=energy)
theta = arange(0, 70, 0.01)
q = xu.Ang2Q(theta)

More information about powdered materials can be obtained from the PowderDiffraction class. It contains information about peak positions and intensities

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
>>> print(xu.simpack.PowderDiffraction(xu.materials.In))
   Powder diffraction object
   -------------------------
   Powder-In (volume: 1, )
   Lattice:
   a1 = (3.252300 0.000000 0.000000), 3.252300
   a2 = (0.000000 3.252300 0.000000), 3.252300
   a3 = (0.000000 0.000000 4.946100), 4.946100
   alpha = 90.000000, beta = 90.000000, gamma = 90.000000
   Lattice base:
   Base point 0: In (49) (0.000000 0.000000 0.000000) occ=1.00 b=0.00
   Base point 1: In (49) (0.500000 0.500000 0.500000) occ=1.00 b=0.00
   Reflections:
   --------------
         h k l     |    tth    |    |Q|    |    Int     |   Int (%)
      ---------------------------------------------------------------
        [0, 1, -1]    32.9338      2.312       217.24      100.00
        [0, 0, -2]    36.2964      2.541        41.69       19.19
        [-1, 1, 0]    39.1392      2.732        67.54       31.09
      [-1, -1, -2]    54.4383      3.731        50.58       23.28
      ....

If you are interested in simulations of powder diffraction patterns look at section Powder diffraction simulations

Using the Gridder classes

xrayutilities provides Gridder classes for 1D, 2D, and 3D data sets. These Gridders map irregular spaced data onto a regular grid. This is often needed after transforming data measured at equally spaced angular positions to reciprocal space were their spacing is irregular.

In 1D this process actually equals the calculation of a histogram. Below you find the most basic way of using the Gridder in 2D. Other dimensions work very similar.

The most easiest use (what most user might need) is:

::
import xrayutilities as xu # import Python package g = xu.Gridder2D(100, 101) # initialize the Gridder object, which will # perform Gridding to a regular grid with 100x101 points #====== load some data here ===== g(x, y, data) # call the gridder with the data griddata = g.data # the data attribute contains the gridded data.

_.. note: previously you could use the Gridder’s gdata object, which was always an internal buffer and should not be used anymore!

A more complicated example showing also sequential gridding is shown below. You need sequential gridding when you can not load all data at the same time, which is often problematic with 3D data sets. In such cases you need to specify the data range before the first call to the gridder.

::

import xrayutilities as xu # import Python package g = xu.Gridder2D(100, 101) # initialize the Gridder object g.KeepData(True) g.dataRange(1, 2, 3, 4) # (xgrd_min, xgrd_max, ygrd_min, ygrd_max) #====== load some data here ===== g(x, y, data) # call the gridder with the data griddata = g.data # the data attribute contains the so far gridded data.

#====== load some more data here ===== g(x, y, data) # call the gridder with the new data griddata = g.data # the data attribute contains the combined gridded data.

Using the material class

xrayutilities provides a set of Python classes to describe crystal lattices and materials.

Examples show how to define a new material by defining its lattice and deriving a new material, furthermore materials can be used to calculate the structure factor of a Bragg reflection for an specific energy or the energy dependency of its structure factor for anomalous scattering. Data for this are taken from a database which is included in the download.

First defining a new material from scratch is shown. This is done from the space group and Wyckhoff positions of the atoms inside the unit cell. Depending on the space group number the initialization of a new SGLattice object expects a different amount of parameters. For a cubic materials only the lattice parameter a should be given while for a triclinic materials a, b, c, alpha, beta, and gamma have to be specified. Its similar for the Wyckoff positions. While some Wyckoff positions require only the type of atom others have some free paramters which can be specified. Below we should the definition of zincblende InP as well as for its hexagonal wurtzite polytype as two examples:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import xrayutilities as xu

# elements (which contain their x-ray optical properties) are loaded from
# xrayutilities.materials.elements
In = xu.materials.elements.In
P = xu.materials.elements.P

# define elastic parameters of the material we use a helper function which
# creates the 6x6 tensor needed from the only 3 free parameters of a cubic
# material.
elastictensor = xu.materials.CubicElasticTensor(10.11e+10, 5.61e+10,
                                                4.56e+10)
# definition of zincblende InP:
InP = xu.materials.Crystal(
    "InP", xu.materials.SGLattice(216, 5.8687, atoms=[In, P],
                                  pos=['4a', '4c']),
    elastictensor)

# a hexagonal equivalent which shows how parameters change for material
# definition with a different space group. Since the elasticity tensor is
# optional its not specified here.
InPWZ = xu.materials.Crystal(
    "InP(WZ)", xu.materials.SGLattice(186, 4.1423, 6.8013,
                                      atoms=[In, P], pos=[('2b', 0),
                                                          ('2b', 3/8.)]))

InP (in both variants) is already included in the xu.materials module and can be loaded by:

InP = xu.materials.InP
InPWZ = xu.materials.InPWZ

Similar definitions exist for many other materials.

Using the material properties the calculation of the reflection strength of a Bragg reflection can be done as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import xrayutilities as xu
import numpy

# defining material and experimental setup
InAs = xu.materials.InAs
energy= 8048 # eV

# calculate the structure factor for InAs (111) (222) (333)
hkllist = [[1, 1, 1], [2, 2, 2], [3, 3, 3]]
for hkl in hkllist:
    qvec = InAs.Q(hkl)
    F = InAs.StructureFactor(qvec, energy)
    print(" |F| = %8.3f" % numpy.abs(F))

Similar also the energy dependence of the structure factor can be determined:

1
2
3
4
5
6
7
8
9
import matplotlib.pyplot as plt

energy= numpy.linspace(500, 20000, 5000) # 500 - 20000 eV
F = InAs.StructureFactorForEnergy(InAs.Q(1, 1, 1), energy)

plt.figure(); plt.clf()
plt.plot(energy, F.real, 'k-', label='Re(F)')
plt.plot(energy, F.imag, 'r-', label='Imag(F)')
plt.xlabel("Energy (eV)"); plt.ylabel("F"); plt.legend()

It is also possible to calculate the components of the structure factor of atoms, which may be needed for input into XRD simulations.:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# f = f0(|Q|) + f1(en) + j * f2(en)
import xrayutilities as xu
import numpy

Fe = xu.materials.elements.Fe # iron atom
Q = numpy.array([0, 0, 1.9], dtype=numpy.double)
en = 10000 # energy in eV

print("Iron (Fe): E: %9.1f eV" % en)
print("f0: %8.4g" % Fe.f0(numpy.linalg.norm(Q)))
print("f1: %8.4g" % Fe.f1(en))
print("f2: %8.4g" % Fe.f2(en))

Calculation of diffraction angles for a general geometry

Often the restricted predefined geometries are not corresponding to the experimental setup, nevertheless xrayutilities is able to calculate the goniometer angles needed to reach a certain reciprocal space position.

For this purpose the goniometer together with the geometric restrictions need to be defined and the q-vector in laboratory reference frame needs to be specified. This works for arbitrary goniometer, however, the user is expected to set up bounds to put restrictions to the number of free angles to obtain reproducible results. In general only three angles are needed to fit an arbitrary q-vector (2 sample + 1 detector angles or 1 sample + 2 detector).

The example below shows the necessary code to perform such an angle calculation for a costum defined material with orthorhombic unit cell.

import xrayutilities as xu
import numpy as np

def Pnma(a, b, c):
    #create orthorhombic unit cell
    l = xu.materials.Lattice([a, 0, 0], [0, b, 0], [0, 0, c])
    return l

latticeConstants=[5.600, 7.706, 5.3995]
SmFeO3 = xu.materials.Crystal("SmFeO3", Pnma(*latticeConstants))
# 2S+2D goniometer
qconv=xu.QConversion(('x+', 'z+'), ('z+', 'x+'), (0, 1, 0))
# [1,1,0] surface normal
hxrd = xu.HXRD(SmFeO3.Q(0, 0, 1), SmFeO3.Q(1, 1, 0), qconv=qconv)

hkl=(2, 0, 0)
q_material = SmFeO3.Q(hkl)
q_laboratory = hxrd.Transform(q_material) # transform

print('SmFeO3: \thkl ', hkl, '\tqvec ', np.round(q_material, 5))
print('Lattice plane distance: %.4f' % SmFeO3.planeDistance(hkl))

#### determine the goniometer angles with the correct geometry restrictions
# tell bounds of angles / (min,max) pair or fixed value for all motors
# maximum of three free motors! here incidence angle fixed to 5 degree
# om, phi, tt, delta
bounds = (5, (-180, 180), (-1, 90), (-1, 90))
ang,qerror,errcode = xu.Q2AngFit(q_laboratory, hxrd, bounds)
print('err %d (%.3g) angles %s' % (errcode, qerror, str(np.round(ang, 5))))
# check that qerror is small!!
print('sanity check with back-transformation (hkl): ',
      np.round(hxrd.Ang2HKL(*ang,mat=SmFeO3),5))

User-specific config file

Several options of xrayutilities can be changed by options in a config file. This includes the default x-ray energy as well as parameters to set the number of threads used by the parallel code and the verbosity of the output.

The default options are stored inside the installad Python module and should not be changed. Instead it is suggested to use a user-specific config file ‘~/.xrayutilities.conf’ or a ‘xrayutilities.conf’ file in the working directory.

An example of such a user config file is shown below:

# begin of xrayutilities configuration
[xrayutilities]

# verbosity level of information and debugging outputs
#   0: no output
#   1: very import notes for users
#   2: less import notes for users (e.g. intermediate results)
#   3: debuging output (e.g. print everything, which could be interesing)
#   levels can be changed in the config file as well
verbosity = 1

# default wavelength in Angstrom,
wavelength = MoKa1 # Molybdenum K alpha1 radiation (17479.374eV)

# default energy in eV
# if energy is given wavelength settings will be ignored
#energy = 10000 #eV

# number of threads to use in parallel sections of the code
nthreads = 1
#   0: the maximum number of available threads will be used (as returned by
#      omp_get_max_threads())
#   n: n-threads will be used

Determining detector parameters

In the following three examples of how to determine the detector parameters for linear and area detectors is given. The procedure we use is in more detail described in this article.

Linear detectors

To determine the detector parameters of a linear detector one needs to perform a scan with the detector angle through the primary beam and aquire a detector spectrum at any point.

Using the following script determines the parameters necessary for the detector initialization, which are:

  • pixelwidth of one channel
  • the center channel
  • and the detector tilt (optional)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
"""
example script to show how the detector parameters
such as pixel width, center channel and detector tilt
can be determined for a linear detector.
"""

import xrayutilities as xu
import os

# load any data file with with the detector spectra of a reference scan
# in the primary beam, here I use spectra measured with a Seifert XRD
# diffractometer
dfile = os.path.join("data", "primarybeam_alignment20130403_2_dis350.nja")
s = xu.io.SeifertScan(dfile)

ang = s.axispos["T"]  # detector angles during the scan
spectra = s.data[:, :, 1]  # detector spectra aquired

# determine detector parameters
# this function accepts some optional arguments to describe the goniometer
# see the API documentation
pwidth, cch, tilt = xu.analysis.linear_detector_calib(ang, spectra,
                                                      usetilt=True)

Area detector (Variant 1)

To determine the detector parameters of a area detector one needs to perform scans with the detector angles through the primary beam and aquire a detector images at any position. For the area detector at least two scans (one with the outer detector and and one with the inner detector angle) are required.

Using the following script determines the parameters necessary for the detector initialization from such scans in the primary beam only. Further down we discuss an other variant which is also able to use additionally detector images recorded at the Bragg reflection of a known reference crystal.

The determined detector parameters are:

  • center channels: position of the primary beam at the true zero position of the goniometer (considering the outer angle offset) (2 parameters)
  • pixelwidth of the channels in both directions (2 parameters), these two parameters can be replaced by the detector distance (1 parameter) if the pixel size is given as an input
  • detector tilt azimuth in degree from 0 to 360
  • detector tilt angle in degree (>0deg)
  • detector rotation around the primary beam in degree
  • outer angle offset, which describes a offset of the outer detector angle from its true zero position

The misalignment parameters as well as the pixel size can be fixed during the fitting.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
"""
example script to show the detector parameter determination for area detectors
from images recorded in the primary beam
"""

import xrayutilities as xu
import os

en = 10300.0  # eV
datadir = os.path.join("data", "wire_")  # data path for CCD files
# template for the CCD file names
filetmp = os.path.join(datadir, "wire_12_%05d.edf.gz")

# manually selected images
# select images which have the primary beam fully on the CCD
imagenrs = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
            20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33]

images = []
ang1 = []
ang2 = []

# read images and angular positions from the data file
# this might differ for data taken at different beamlines since
# they way how motor positions are stored is not always consistent
for imgnr in imagenrs:
    filename = filetmp % imgnr
    edf = xu.io.EDFFile(filename)
    images.append(edf.data)
    ang1.append(float(edf.header['ESRF_ID01_PSIC_NANO_NU']))
    ang2.append(float(edf.header['ESRF_ID01_PSIC_NANO_DEL']))


# call the fit for the detector parameters
# detector arm rotations and primary beam direction need to be given.
# in total 9 parameters are fitted, however the severl of them can
# be fixed. These are the detector tilt azimuth, the detector tilt angle, the
# detector rotation around the primary beam and the outer angle offset
# The detector pixel size or the detector distance should be kept unfixed to
# be optimized by the fit.
param, eps = xu.analysis.sample_align.area_detector_calib(
    ang1, ang2, images, ['z+', 'y-'], 'x+',
    start=(None, None, 1.0, 45, 0, -0.7, 0),
    fix=(False, False, True, False, False, False, False),
    wl=xu.en2lam(en))

A possible output of this script could be

fitted parameters: epsilon: 8.0712e-08 (2,[‘Parameter convergence’]) param: (cch1,cch2,pwidth1,pwidth2,tiltazimuth,tilt,detrot,outerangle_offset) param: 140.07 998.34 4.4545e-05 4.4996e-05 72.0 1.97 -0.792 -1.543 please check the resulting data (consider setting plot=True) detector rotation axis / primary beam direction (given by user): [‘z+’, ‘y-‘] / x+ detector pixel directions / distance: z- y+ / 1 detector initialization with: init_area(‘z-‘, ‘y+’, cch1=140.07, cch2=998.34, Nch1=516, Nch2=516, pwidth1=4.4545e-05, pwidth2=4.4996e-05, distance=1., detrot=-0.792, tiltazimuth=72.0, tilt=1.543) AND ALWAYS USE an (additional) OFFSET of -1.9741deg in the OUTER DETECTOR ANGLE!

The output gives the fitted detector parameters and compiles the Python code line one needs to use to initialize the detector. Important to note is that the outer angle offset which was determined by the fit (-1.9741 degree in the aboves example) is not included in the initialization of the detector parameters but needs to be used in every call to the q-conversion function as offset. This step needs to be performed manually by the user!

Area detector (Variant 2)

In addition to scans in the primary beam this variant enables also the use of detector images recorded in scans at Bragg reflections of a known reference materials. However this also required that the sample orientation and x-ray wavelength need to be fit. To keep the additional parameters as small as possible we only implemented this for symmetric coplanar diffractions.

The advantage of this method is that it is more sensitive to the outer angle offset also at large detector distances. The additional parameters are:

  • sample tilt angle in degree
  • sample tilt azimuth in degree
  • and the x-ray wavelength in Angstrom
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
"""
example script to show the detector parameter determination for area detectors
from images recorded in the primary beam and at known symmetric coplanar Bragg
reflections of a reference crystal
"""

import xrayutilities as xu
import os
import numpy

Si = xu.materials.Si

datadir = 'data'
specfile = "si_align.spec"

en = 15000  # eV
wl = xu.en2lam(en)
imgdir = os.path.join(datadir, "si_align_")  # data path for CCD files
filetmp = "si_align_12_%04d.edf.gz"

qconv = xu.QConversion(['z+', 'y-'], ['z+', 'y-'], [1, 0, 0])
hxrd = xu.HXRD(Si.Q(1, 1, -2), Si.Q(1, 1, 1), wl=wl, qconv=qconv)

# manually selected images

s = xu.io.SPECFile(specfile, path=datadir)
for num in [61, 62, 63, 20, 21, 26, 27, 28]:
    s[num].ReadData()
    try:
        imagenrs = numpy.append(imagenrs, s[num].data['ccd_n'])
    except:
        imagenrs = s[num].data['ccd_n']

# avoid images which do not have to full beam on the detector as well as
# other which show signal due to cosmic radiation
avoid_images = [37, 57, 62, 63, 65, 87, 99, 106, 110, 111, 126, 130, 175,
                181, 183, 185, 204, 206, 207, 208, 211, 212, 233, 237, 261,
                275, 290]

images = []
ang1 = []  # outer detector angle
ang2 = []  # inner detector angle
sang = []  # sample rocking angle
hkls = []  # Miller indices of the reference reflections


def hotpixelkill(ccd):
    """
    function to remove hot pixels from CCD frames
    ADD REMOVE VALUES IF NEEDED!
    """
    ccd[304, 97] = 0
    ccd[303, 96] = 0
    return ccd

# read images and angular positions from the data file
# this might differ for data taken at different beamlines since
# they way how motor positions are stored is not always consistent
for imgnr in numpy.sort(list(set(imagenrs) - set(avoid_images))[::4]):
    filename = os.path.join(imgdir, filetmp % imgnr)
    edf = xu.io.EDFFile(filename)
    ccd = hotpixelkill(edf.data)
    images.append(ccd)
    ang1.append(float(edf.header['motor_pos'].split()[4]))
    ang2.append(float(edf.header['motor_pos'].split()[3]))
    sang.append(float(edf.header['motor_pos'].split()[1]))
    if imgnr > 1293.:
        hkls.append((0, 0, 0))
    elif imgnr < 139:
        hkls.append((0, 0, numpy.sqrt(27)))  # (3,3,3))
    else:
        hkls.append((0, 0, numpy.sqrt(75)))  # (5,5,5))

# call the fit for the detector parameters.
# Detector arm rotations and primary beam direction need to be given
# in total 8 detector parameters + 2 additional parameters for the reference
# crystal orientation and the wavelength are fitted, however the 4 misalignment
# parameters of the detector and the 3 other parameters can be fixed.
# The fixable parameters are detector tilt azimuth, the detector tilt angle,
# the detector rotation around the primary beam, the outer angle offset, sample
# tilt, sample tilt azimuth and the x-ray wavelength
# Additionally if accurately known the detector pixel size can be given and
# fixed and instead the detector distance can be fitted.
param, eps = xu.analysis.area_detector_calib_hkl(
    sang, ang1, ang2, images, hkls, hxrd, Si, ['z+', 'y-'], 'x+',
    start=(None, None, 1.0, 45, 1.69, -0.55, -1.0, 1.3, 60., wl),
    fix=(False, False, True, False, False, False, False, False, False, False),
    plot=True)

# Following is an example of the output of the summary of the
# area_detector_calib_hkl function
# total time needed for fit: 624.51sec
# fitted parameters: epsilon: 9.9159e-08 (2,['Parameter convergence'])
# param:
# (cch1,cch2,pwidth1,pwidth2,tiltazimuth,tilt,detrot,outerangle_offset,
# sampletilt,stazimuth,wavelength)
# param: 367.12 349.27 6.8187e-05 6.8405e-05 131.4 2.87 -0.390 -0.061 1.201
# 318.44 0.8254
# please check the resulting data (consider setting plot=True)
# detector rotation axis / primary beam direction (given by user): ['z+', 'y-']
# / x+
# detector pixel directions / distance: z- y+ / 1
#   detector initialization with:
#   init_area('z-', 'y+', cch1=367.12, cch2=349.27, Nch1=516, Nch2=516,
#   pwidth1=6.8187e-05, pwidth2=6.8405e-05, distance=1., detrot=-0.390,
#   tiltazimuth=131.4, tilt=2.867)
# AND ALWAYS USE an (additional) OFFSET of -0.0611deg in the OUTER
# DETECTOR ANGLE!