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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
#!/usr/bin/env python3
"""
Fitting example: running same fit using various minimizer and their settings.
"""
import bornagain as ba
from bornagain import deg, angstrom, nm
def get_sample(params):
"""
Returns a sample with uncorrelated cylinders and prisms on a substrate.
"""
cylinder_height = params["cylinder_height"]
cylinder_radius = params["cylinder_radius"]
prism_height = params["prism_height"]
prism_base_edge = params["prism_base_edge"]
# defining materials
m_vacuum = ba.RefractiveMaterial("Vacuum", 0, 0)
m_substrate = ba.RefractiveMaterial("Substrate", 6e-6, 2e-8)
m_particle = ba.RefractiveMaterial("Particle", 6e-4, 2e-8)
# collection of particles
cylinder_ff = ba.Cylinder(cylinder_radius, cylinder_height)
cylinder = ba.Particle(m_particle, cylinder_ff)
prism_ff = ba.Prism3(prism_base_edge, prism_height)
prism = ba.Particle(m_particle, prism_ff)
layout = ba.ParticleLayout()
layout.addParticle(cylinder, 0.5)
layout.addParticle(prism, 0.5)
interference = ba.InterferenceNone()
layout.setInterference(interference)
# vacuum layer with particles and substrate form multi layer
vacuum_layer = ba.Layer(m_vacuum)
vacuum_layer.addLayout(layout)
substrate_layer = ba.Layer(m_substrate, 0)
sample = ba.MultiLayer()
sample.addLayer(vacuum_layer)
sample.addLayer(substrate_layer)
return sample
def get_simulation(params):
"""
Returns a GISAXS simulation with beam and detector defined
"""
beam = ba.Beam(1e8, 1*angstrom, 0.2*deg)
n = 100 # bp.simargs['n']
detector = ba.SphericalDetector(n, -1*deg, 1*deg, n, 0, 2*deg)
return ba.ScatteringSimulation(beam, get_sample(params), detector)
def create_real_data():
"""
Generating "real" data from simulated image with default parameters.
"""
params = {
'cylinder_height': 5*nm,
'cylinder_radius': 5*nm,
'prism_height': 5*nm,
'prism_base_edge': 5*nm
}
simulation = get_simulation(params)
result = simulation.simulate()
return result.array()
def run_fitting():
"""
main function to run fitting
"""
real_data = create_real_data()
# prints info about available minimizers
print(ba.MinimizerFactory().catalogToString())
# prints detailed info about available minimizers and their options
print(ba.MinimizerFactory().catalogDetailsToString())
fit_objective = ba.FitObjective()
fit_objective.addSimulationAndData(get_simulation, real_data, 1)
fit_objective.initPrint(10)
params = ba.Parameters()
params.add("cylinder_height", 4.*nm, min=0.01)
params.add("cylinder_radius", 6.*nm, min=0.01)
params.add("prism_height", 4.*nm, min=0.01)
params.add("prism_base_edge", 12.*nm, min=0.01)
minimizer = ba.Minimizer()
# Uncomment one of the line below to adjust minimizer settings
"""
Setting Minuit2 minimizer with Migrad algorithm, limiting number of iterations.
Minimization will try to respect MaxFunctionCalls value.
"""
# minimizer.setMinimizer("Minuit2", "Migrad", "MaxFunctionCalls=50")
"""
Setting two options at once.
Strategy=2 promises more accurate fit.
"""
# minimizer.setMinimizer("Minuit2", "Migrad", "MaxFunctionCalls=500;Strategy=2")
"""
Setting Minuit2 minimizer with Fumili algorithm.
"""
# minimizer.setMinimizer("Minuit2", "Fumili")
"""
Setting Levenberg-Marquardt algorithm.
"""
# minimizer.setMinimizer("GSLLMA")
result = minimizer.minimize(fit_objective.evaluate_residuals, params)
fit_objective.finalize(result)
print("Fitting completed.")
print("chi2:", result.minValue())
for fitPar in result.parameters():
print(fitPar.name(), fitPar.value, fitPar.error)
if __name__ == '__main__':
run_fitting()
|