fit sample

[1]:
import nexus as nx
import numpy as np
import matplotlib.pyplot as plt

load data

[2]:
data = np.loadtxt('sample_spectrum.dat')
processed_data = data

velocities = np.loadtxt("velocity_calibration.dat")
[3]:
plt.plot(data)
plt.xlabel("data point")
plt.ylabel("counts")
plt.show()

print('number of data points: {}'.format(len(data)))
../../_images/examples_Moessbauer_Spectrum_nb_fit_sample_4_0.png
number of data points: 1024

data folding

[4]:
# use same settings as for velocity calibration

processed_data, lag = nx.data.AutoFold(processed_data,
                                       flip = 'right',  # determies which part of the spectrum is fliped
                                       factor = 100,   # interpolation on a 100 times finer grid
                                       method = "cubic") # or cubic, PChip, Akima

print('lag: {}'.format(lag))
lag: -1.13

normalization

[5]:
processed_data, norm_factor = nx.data.Normalize(data = processed_data,
                                                method = "baseline",   # 'max', 'min', 'value'
                                                # value = 0,            # only used when method is value
                                                # left_point=20,
                                                # right_point=250,
                                                # poly_order = 1        # use 1 in case you want to correct a tilted baseline
                                                )
[6]:
plt.plot(velocities, processed_data)
plt.xlabel("velocitiy (mm/s)")
plt.ylabel("data point")
plt.show()
../../_images/examples_Moessbauer_Spectrum_nb_fit_sample_9_0.png

setup experiment

hyperfine sites

[7]:
# we assume to have two sites and an isotropic distribution (e.g. powder sample)

site1 = nx.Hyperfine(id = "site 1",
                     weight = nx.Var(1, min = 0, max = 1, fit = True, id = "weight 1"),
                     isomer = nx.Var(0, min = -1, max = 0, fit = True, id = "isomer 1"),
                     magnetic_field = nx.Var(15, min = 10, max = 25, fit = True, id = "magnetic field 1"),
                     quadrupole = nx.Var(0, min = -2, max = 2, fit = True, id = "quadrupole 1"),
                     # texture = 1,     # only used if isotropic = False
                     # broadening = 1   # model free broadening parameter
                     isotropic = True)

site2 = nx.Hyperfine(id = "site 2",
                     weight = nx.Var(1, min = 0, max = 1, fit = True, id = "weight 2"),
                     isomer = nx.Var(0, min = -1, max = 1, fit = True, id = "isomer 2"),
                     magnetic_field = 0,
                     quadrupole = nx.Var(0, min = -1, max = 1, fit = True, id = "quadrupole 2"),
                     isotropic = True)

sample and experiment

always fit the sample thickness for a Moessabuer experiment

it serves as the scaling factor for the absorption

[8]:
# always fit the sample thickness, it serves as the scaling factor for the absorption

sample = nx.SimpleSample(id = "sample",
                         thickness = nx.Var(3000, min=2000, max=4000, fit=True, id="t"),
                         composition = [["Fe", 1]],
                         density = 7.874,
                         isotope = nx.lib.moessbauer.Fe57,
                         abundance = 0.02119,
                         lamb_moessbauer = 0.796,
                         hyperfine_sites = [site1, site2]
                        )

beam = nx.Beam(id = "upolarized beam",
               polarization = 0,
               # mixing_angle = 0,   # ony for polarized beams
               # canting_angle = 0   # only for polarized beams
               # profile = "g"       # only for grazing incidence
               # fwhm = 0            # only for grazing incidence
               )

#beam.Unpolarized()   # not needed as polarization has been set to zero

exp = nx.Experiment(id = "experiment",
                    beam = beam,
                    objects = [sample],
                    isotope = nx.lib.moessbauer.Fe57)

spectrum = nx.MoessbauerSpectrum(id= "Moessbauer spectrum",
                                 experiment = exp,
                                 velocity = velocities,
                                 intensity_data = processed_data,
                                 # scaling = "auto"       # use a Var in csae you want to specify this value
                                 # background = "auto"    # use a Var in csae you want to specify this value
                                 # residual = nx.lib.residual.Sqrt()   # in case you want to change to Gaussian statistics use nx.lib.residual.StdDev()
                                 # kernel_type = "Lorentz"   # is standard for Moessbauer spectrum
                                 resolution = 2.197)  # value from calibration file or fit value as Var object

intensity = spectrum.Calculate()

spectrum.Plot(velocity = True
              # name=None    # provide a filename string to save under this filename
              # data=True,
              # residuals=True,
              # datacolor='black'
              # ... many more style options, see API
              # legend=True,
              # errors=False,
              # errorcap=2
              )
../../_images/examples_Moessbauer_Spectrum_nb_fit_sample_15_0.png

setup fit

[9]:
# create a fit object with a list of measurements to be fit in parallel.
fit = nx.Fit(id = "fit sample",
             measurements = [spectrum],
             # external_fit_variables = []    # for advanced fitting
             # einequalities = None           # for advanced fitting
            )

fit.options.method = "PagmoDiffEvol"   # or ""LevMar", "Annealing", "NaturalEvol", ...
# many fit.options area vailable, see API

# run the fit
fit.Evaluate()

# the fit creates an output file "fit_id_fit sample.txt" with the results

Run Fit instance with id: fit sample

Starting fit with 1 measurement data set(s) and 10 fit parameter(s):

  no. |                           id |       initial value |              min |              max
    0 |                   ES scaling |             1.00718 |            1e-16 |          100.718
    1 |                    ES backgr |            0.099213 |                0 |           9.9213
    2 |                            t |                3000 |             2000 |             4000
    3 |                     weight 1 |                 0.5 |                0 |                1
    4 |                     isomer 1 |                   0 |               -1 |                0
    5 |             magnetic field 1 |                  15 |               10 |               25
    6 |                 quadrupole 1 |                   0 |               -2 |                2
    7 |                     weight 2 |                 0.5 |                0 |                1
    8 |                     isomer 2 |                   0 |               -1 |                1
    9 |                 quadrupole 2 |                   0 |               -1 |                1

Using 0 equality constraint(s) on parameter(s):

Using 0 inequality constraint(s).


Calling Pagmo solver with fit method PagmoDiffEvol

  population: 110
  iterations: 100

  cost = 1.540153e-03

Calling ceres solver with fit method LevMar

Ceres Solver Report: Iterations: 18, Initial cost: 1.540153e-03, Final cost: 2.628095e-04, Termination: CONVERGENCE

Gradient error analysis.


Fit performed with algorithm:
PagmoDiffEvol
Local algorithm:
LevMar
Error analysis:
Gradient

Using 10 fit parameter(s):

  no. |                           id |          fit value |   +/- std dev | initial value |          min |          max
    0 |                   ES scaling |             1.1545 |    0.00680461 |       1.00718 |        1e-16 |      100.718
    1 |                    ES backgr |          0.0084713 |    0.00579641 |      0.099213 |            0 |       9.9213
    2 |                            t |            3077.27 |    3.7684e-06 |          3000 |         2000 |         4000
    3 |                     weight 1 |           0.500168 |    0.00338044 |           0.5 |            0 |            1
    4 |                     isomer 1 |          -0.503887 |     0.0023586 |             0 |           -1 |            0
    5 |             magnetic field 1 |            19.9924 |     0.0166707 |            15 |           10 |           25
    6 |                 quadrupole 1 |            1.40606 |     0.0068483 |             0 |           -2 |            2
    7 |                     weight 2 |           0.499832 |    0.00338271 |           0.5 |            0 |            1
    8 |                     isomer 2 |           0.725443 |    0.00167479 |             0 |           -1 |            1
    9 |                 quadrupole 2 |          -0.800297 |    0.00313628 |             0 |           -1 |            1

Correlation matrix:

  no. |        0        1        2        3        4        5        6        7        8        9
 -----|------------------------------------------------------------------------------------------
    0 |    1.000   -1.000    1.000    0.350    0.004    0.015   -0.005   -0.350   -0.019   -0.123
    1 |   -1.000    1.000   -1.000   -0.350   -0.004   -0.015    0.005    0.350    0.019    0.123
    2 |    1.000   -1.000    1.000    0.353    0.004    0.016   -0.005   -0.353   -0.019   -0.123
    3 |    0.350   -0.350    0.353    1.000   -0.040   -0.022    0.034   -1.000   -0.032   -0.080
    4 |    0.004   -0.004    0.004   -0.040    1.000    0.308   -0.372    0.040    0.188    0.033
    5 |    0.015   -0.015    0.016   -0.022    0.308    1.000   -0.231    0.022    0.138    0.021
    6 |   -0.005    0.005   -0.005    0.034   -0.372   -0.231    1.000   -0.034   -0.212   -0.038
    7 |   -0.350    0.350   -0.353   -1.000    0.040    0.022   -0.034    1.000    0.032    0.080
    8 |   -0.019    0.019   -0.019   -0.032    0.188    0.138   -0.212    0.032    1.000    0.007
    9 |   -0.123    0.123   -0.123   -0.080    0.033    0.021   -0.038    0.080    0.007    1.000

Using 0 equality constraint(s) on parameter(s):

and 0 inequality constraint(s).

Total cost = 2.627e-04

cost for each FitMeasurement is:

  no. |                           id |                cost |                   %
    0 |          Moessbauer spectrum |           2.627e-04 |             100.000

Fit instance with id "fit sample" finished.

[10]:
spectrum.Plot(velocity=True)

# in case the fit does not look good:
# restart when a global mehtod has been used (as here), maybe change parameter boundaries
# when a local method has been used, change start parameter (maybe also parameter boundaries)
../../_images/examples_Moessbauer_Spectrum_nb_fit_sample_18_0.png

plot individual contributions

[11]:
plt.plot(velocities, processed_data, '.', color='black')

#get intensities from individual components
intensity_sites = nx.tools.GetSiteSpectra(spectrum, sample)

# intensity_sites is a 3D array with first dimension being the layer, the second the site of the layer, and the 3rd the intensity
print(np.array(intensity_sites).shape)   # 1 layer, 2 sites, 512 data points

# go through intensity and layers of the sample in parallel to get intensity and id of the site
for intens_lay, lay in zip(intensity_sites, sample.layers):
    for intens_site, site in zip(intens_lay, lay.material.hyperfine_sites):
        plt.plot(velocities, intens_site, label = site.id)
        plt.fill_between(velocities, intens_site, intens_site[0], alpha = 0.3)

plt.plot(velocities, spectrum.result, color='red', label = "total fit")
plt.xlabel("norm. intensity")
plt.ylabel("velocitiy (mm/s)")
plt.legend()
plt.show()
(1, 2, 512)
../../_images/examples_Moessbauer_Spectrum_nb_fit_sample_20_1.png

area ratios of intensities

[12]:
area_sites = nx.tools.AreaSites(spectrum,
                                sample = sample,
                                norm = True)

print(np.array(area_sites).shape)   # 1 layer, 2 sites

print("")
print("Relative areas of the sites")
print(area_sites)
(1, 2)

Relative areas of the sites
[[0.6662509 0.3337491]]
[ ]: