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
109
110
111
112
113
|
#!/usr/bin/env python3
# /// script
# requires-python = ">=3.10"
# dependencies = ["bornagain>=25,<26", "lmfit"]
# ///
"""
Basic example how to fit specular data.
The sample consists of twenty alternating Ti and Ni layers.
Reference data was generated with GenX.
We fit just one parameter, the thickness of the Ti layers,
which has an original value of 30 angstroms.
"""
import bornagain as ba
ba.require_versions("bornagain>=25,<26")
import os
from bornagain import angstrom, nm
import lmfit
import numpy as np
def load_data():
# By default, read data files from the script directory.
datadir = ba.data_dir(beside=__file__)
fname = os.path.join(datadir, "genx_alternating_layers.dat.gz")
two_alpha, intensity = ba.read_columns(fname, usecols=(0, 1))
alpha = 0.5*two_alpha*ba.deg
return alpha, intensity
def datafield_from_arrays(x, y):
frame = ba.Frame(ba.ListScan("alpha_i (rad)", x))
return ba.Datafield(frame, y.tolist())
def get_sample(P):
# Materials
ti_color = (0.05, 0.62, 0.55)
ti_mat = ba.SLDMaterial("Ti", ti_color, -1.9493e-06, 0)
ni_color = (0.93, 0.48, 0.14)
ni_mat = ba.SLDMaterial("Ni", ni_color, 9.4245e-06, 0)
si_color = (0.30, 0.62, 0.86)
si_mat = ba.SLDMaterial("Si", si_color, 2.0704e-06, 0)
# Layers
layer_Ti = ba.Layer(ti_mat, P["thickness_Ti"])
layer_Ni = ba.Layer(ni_mat, 70*angstrom)
# Periodic stack
n_repetitions = 10
stack = ba.LayerStack(n_repetitions)
stack.addLayer(layer_Ti)
stack.addLayer(layer_Ni)
# Sample
sample = ba.Sample()
sample.addLayer(ba.Layer(ba.Vacuum()))
sample.addStack(stack)
sample.addLayer(ba.Layer(si_mat))
return sample
def get_simulation(alpha_axis, P):
scan = ba.AlphaScan(alpha_axis)
scan.setWavelength(1.54*angstrom)
sample = get_sample(P)
return ba.SpecularSimulation(scan, sample)
if __name__ == '__main__':
alpha, exp_values = load_data()
exp_data = datafield_from_arrays(alpha, exp_values)
# Fit progress display
fit_plotter = ba.FitPlotter(
ba.plot_specular,
context_data=exp_data,
ylabel="Intensity",
)
monitor = ba.FitMonitor(
fit_plotter,
ncols=1,
show_best=True,
max_fps=1,
printer=ba.Printer(every_nth=10),
live=True)
def residuals(P):
"""
Simulates, reports, and returns the residual vector.
"""
sim_result = get_simulation(alpha, P.valuesdict()).simulate()
residuals = exp_values - sim_result.intensities()
monitor.update(sim_result, P, residuals)
return residuals
P = lmfit.Parameters()
P.add("thickness_Ti", value=5*nm, min=1*nm, max=6*nm)
result = lmfit.minimize(residuals, P, method="leastsq")
finalP = result.params.valuesdict()
# Recompute and report the simulation at the fitted parameters.
residuals(result.params)
# Render the just-reported evaluation as the final fit state.
monitor.render_final(result.params)
print(lmfit.fit_report(result))
ba.showSample3D(get_sample(finalP), sample_size=120*nm, seed=0)
ba.plt.show()
|