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
129
130
|
#!/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.HomogeneousMaterial("Vacuum", 0, 0)
m_substrate = ba.HomogeneousMaterial("Substrate", 6e-6, 2e-8)
m_particle = ba.HomogeneousMaterial("Particle", 6e-4, 2e-8)
# collection of particles
cylinder_ff = ba.FormFactorCylinder(cylinder_radius, cylinder_height)
cylinder = ba.Particle(m_particle, cylinder_ff)
prism_ff = ba.FormFactorPrism3(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.InterferenceFunctionNone()
layout.setInterferenceFunction(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)
multi_layer = ba.MultiLayer()
multi_layer.addLayer(vacuum_layer)
multi_layer.addLayer(substrate_layer)
return multi_layer
def get_simulation(params):
"""
Returns a GISAXS simulation with beam and detector defined
"""
simulation = ba.GISASSimulation()
simulation.setDetectorParameters(100, -1*deg, 1*deg, 100, 0, 2*deg)
simulation.setBeamParameters(1*angstrom, 0.2*deg, 0)
simulation.beam().setIntensity(1e+08)
simulation.setSample(get_sample(params))
return simulation
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)
simulation.runSimulation()
return simulation.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()
|