import numpy as np
import escape as esc
esc.require("0.9.7")
from escape.utils.widgets import show
Loading material database from /home/dkor/Data/Development/workspace_escape/escape/python/src/escape/scattering/../data/mdb/materials.db
Specular reflectivity. (Ta/Cu/Nb/Gd/Nb/Al2O3). Example with mass density fit.¶
In this notebook we are going to fit the data published in the following article [https://doi.org/10.1103/PhysRevB.97.144511]. The given sample is a Nb(25nm)/Gd(7nm)/Nb(25nm) trilayer on a Al2O3 substrate and covered with Ta/Cu thin films. In the previous notebook about specular reflectivity we have demonstarted how the module works, where as an experimental data we took the curve generated with Parratt32 program. The idea of this notebook is to demonstrate how to work with real experimental data in terms of ESCAPE. The result obtained here could be slightly different from what is published, because we do not have all details about published model.
We first define our parameters, which we are going to fit. The names of these parameters are self-explanatory.
thknTa = esc.par("Ta Thkn", 3.0, units="nm", userlim=[1, 5])
roughTa = esc.par("Ta Roughness", 0.1, units="nm", userlim=[0, 3])
thknCu = esc.par("Cu Thkn", 4.0, units="nm", userlim=[3, 7])
roughCu = esc.par("Cu Roughness", 0.5, units="nm", userlim=[0, 4])
thknGd = esc.par("Gd Thkn", 7.6, units="nm", userlim=[0, 10])
roughGd = esc.par("Gd Roughness", 0.5, units="nm", userlim=[0, 5])
thknNb1 = esc.par("Nb1 Thkn", 25.0, units="nm", userlim=[20, 30])
roughNb1 = esc.par("Nb1 Roughness", 1.0, units="nm", userlim=[0, 5])
thknNb2 = esc.par("Nb2 Thkn", 25.0, units="nm", userlim=[20, 30])
roughNb2 = esc.par("Nb2 Roughness", 1.0, units="nm", userlim=[0, 5])
roughAl2O3 = esc.par("Al2O3 Roughness", 1.0, units="nm", userlim=[0, 1])
Next we create materials, layers and finally the sample objects.
Al2O3=esc.amorphous("Al2O3", formula="Al2O3", density=esc.par(value="3.95g/cm^3", fixed=True))
ta_layer = esc.layer("Layer: Ta", "Ta", thknTa, roughTa, bydensity=True)
cu_layer = esc.layer("Layer: Cu", "Cu", thknCu, roughCu, bydensity=True)
gd_layer = esc.layer("Layer: Gd", "Gd", thknGd, roughGd, bydensity=True)
nb1_layer = esc.layer("Layer: Nb1", "Nb", thknNb1, roughNb1, bydensity=True)
nb2_layer = esc.layer("Layer: Nb2", "Nb", thknNb2, roughNb2, bydensity=True)
sub = esc.substrate("Substrate: Al2O3", Al2O3, roughAl2O3, bydensity=True)
sample = esc.multilayer("Ta/Cu/Nb1/Gd/Nb2/Al2O3", frgr=esc.air("Air"), bkgr=sub)
sample.add(ta_layer)
sample.add(cu_layer)
sample.add(nb1_layer)
sample.add(gd_layer)
sample.add(nb2_layer)
wl = 0.229 #we use it later
src = esc.xrays(wavelength=wl, units="nm")
show(sample, source=src)
The contribution of resolution function is negligible, but as a demonstration we will take it into account. The beam FWHM can be also defined as a parameter. This can help, if resolution function cannot be estimated properly. Normally this paramater is fixed.
Qz=esc.var("qz")
Qz0=esc.var("qz0")
fwhm=esc.par("FWHM", 2.0e-2, userlim=[0.001, 0.02], fixed=True)
R = esc.specrefl("Specrefl", Qz, sample, "matrix", source=src)
#we integrate over the variable Qz, thus intensity is a function of I(Qz0)
R = esc.average_normal(R, fwhm, Qz, Qz0)
Below we take into account sample size. At low angles the X-rays or neutrons beam doesn't cover the whole sample leading to the reduction of intensity. Usually this correction is done for the data, but can be also added to the model as a fit parameter in the case if some characteristics are not known.
I0=esc.par("I0", 1, scale=1e7, userlim=[0.1, 10], units="Cnt")
B=esc.par("Bgr", 10, userlim=[0, 30], units="Cnt")
h=0.05 #size of a beam
L=5 # size of a sample
Qmax=esc.par("Qmax", 4*np.pi/wl*h/L, fixed=True, userlim=[0, 1.5], units="1/nm")
I0f=esc.conditional(Qz0>Qmax, esc.func("", Qz0, I0), I0/(Qmax/Qz0))
I=I0f*R+B
#Opening the experimental data and creating the data object
theta, y=np.loadtxt("data/GdNb/TriLayer/XRR/xrr2.dat", unpack=True)
qz=4*np.pi/wl*np.sin((theta/2)*np.pi/180.0)
qz_r=qz[(qz>=0.15) & (qz<=3)]
y_r=y[(qz>=0.15) & (qz<=3)]
dobj = esc.data("Trilayer.dat", qz_r, y_r, copy=True)
#Creating the model
mobj = esc.model("Model", I, dobj, residuals_scale="q4", weight_type="data")
#show(mobj, ylog=True, xlog=False, xlabel="Q[1/nm]", ylabel="R")
#Now we can perform optimization with DE algorithm
opt = esc.diffevol("DiffEvol", mobj, popsize=7, maxiter=150,
mutation=0.5, crossover=0.5, minconv=1e-3, mincost=1e-10,
nupdate=5,
polish_final_maxiter=150, polish_candidate_maxiter=0)
#opt()
show(opt, ylog=True, xlog=False, xlabel="Q[1/nm]", ylabel="R")
show(sample, source=src)
opt
Name: DiffEvol Parameters number: 21 Parameter Value +- Error Units Fixed Qmax 0.54875 +- 0 1/nm 1 I0 1.1219 +- 0.00030921 x1e+07 Cnt 0 FWHM 0.02 +- 0 1 Ta density 15.527 +- 0.002966 g/cm^3 0 Ta Thkn 4.4447 +- 0.0030013 nm 0 Ta Roughness 0.99929 +- 0.00022683 nm 0 Cu density 6.1473 +- 0.017325 g/cm^3 0 Cu Thkn 3 +- 0 nm 0 Cu Roughness 3.2912 +- 0.0049973 nm 0 Nb density 8.9715 +- 0.0014459 g/cm^3 0 Nb1 Thkn 25.511 +- 0.0031011 nm 0 Nb1 Roughness 1.6468 +- 0.0037594 nm 0 Gd density 3.9505 +- 0 g/cm^3 0 Gd Thkn 7.4189 +- 0.0016084 nm 0 Gd Roughness 0.8965 +- 0.0005379 nm 0 Nb density 12.834 +- 0.0029957 g/cm^3 0 Nb2 Thkn 22.92 +- 0.0017932 nm 0 Nb2 Roughness 1.8646 +- 0.0012018 nm 0 Al2O3 Density 3.95 +- 0 g/cm^3 1 Al2O3 Roughness 0.67833 +- 0.00025919 nm 0 Bgr 0 +- 0 Cnt 0