-
Notifications
You must be signed in to change notification settings - Fork 3
/
fitfermions.py
321 lines (244 loc) · 11.1 KB
/
fitfermions.py
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
#!/usr/bin/env python
"""A high level interface to the temperature fit routines for fermions.
The easiest to use function is fit_img(). When a single transmission image
is passed in to this function, the fit should just work. Normalization
is automatically taken care of. It is assumed that the atom cloud is
azimuthally symmetric around its center of mass. If this is not the case,
find_ellipticity() should be used to find the aspect ratio of the cloud first.
"""
import numpy as np
import scipy as sp
from scipy import optimize
from centerofmass import center_of_mass
import imageio
from imageprocess import *
from visualize import *
from fitfuncs import *
def average_images(imgs, roi=None):
"""Import one or more images and average them.
**Inputs**
* imgs: str or list, containing the paths to image files to be averaged.
a single image is not averaged but simply opened.
* roi: sequence of two slices, defining the region of interest.
**Outputs**
* od_avg: 2D array, containing the averaged OD image.
* trans_avg: 2D array, containing the averaged transmission image.
"""
odlist = []
translist = []
if isinstance(imgs, str):
imgs = [imgs]
try:
imgs.remove(None)
except ValueError:
pass
for img in imgs:
img_array = imageio.imgimport_intelligent(img)
transimg, odimg = calc_absimage(img_array)
if roi:
transimg = transimg[roi]
odimg = trans2od(transimg)
odlist.append(odimg)
translist.append(transimg)
od_avg = np.mean(np.array(odlist), axis=0)
trans_avg = np.mean(np.array(translist), axis=0)
return od_avg, trans_avg
def norm_and_guess(transimg, norm=True):
"""Normalize the transmission image and find initial fitting parameters"""
odimg = trans2od(transimg)
# determine CoM to use as parameter in fitting of n2D_radial to odimg
com = center_of_mass(odimg)
# guess initial fit parameters
n0 = odimg[com[0]-5:com[0]+5, com[1]-5:com[1]+5].sum()*1e-2 # av. central OD
a = 4 # log(fugacity)
# approximate cloud size, with minimum 10 pixels in case this does not work
# use something better, like moment estimation!
# for elliptical clouds this is way off, and for low OD it fails!
bprime = 0.5*(threshold_image(odimg.mean(axis=1), thres=0.1*n0, below=False)).sum()
bprime = max(bprime, 10)
# normalize the background
if norm:
try:
transimg = normalize_img(transimg, com, bprime)
except NotImplementedError:
print "Couldn't normalize the image"
# this maxod is specific to each experiment and needs to be checked
odimg = trans2od(transimg, maxod=3)
com = center_of_mass(odimg)
return transimg, odimg, com, n0, a, bprime
def find_ellipticity(transimg, normalize=True, tol=2e-3):
"""Determines the ellipticity of an atom cloud
The ellipticity is found by an optimization routine. A value is tried
and the sum of residuals between the radial line profiles and the averaged
result is computed. This value is then minimized. This method is accurate
but slow. An alternative option is to use the second moments of the image.
**Inputs**
* transimg: 2D array, containing the absorption image
* normalize: bool, if True transimg is normalized with norm_and_guess(),
otherwise the odimg is obtained directly from transimg.
* tol: float, the required tolerance
**Outputs**
* ellip: float, the ellipticity of the atom cloud
"""
if normalize:
transimg, odimg, com, n0, a, bprime = norm_and_guess(transimg)
else:
odimg = trans2od(transimg)
com = center_of_mass(odimg)
def ellipticity(ell):
rcoord, rad_profile, rprofiles, angles = radial_interpolate(odimg, com,\
0.3, elliptic=(ell, 0), full_output=True)
od_cutoff = find_fitrange(rad_profile)
av_err = radialprofile_errors(rprofiles, angles, rad_profile, \
od_cutoff, showfig=False, report=False)
return av_err
ellip = sp.optimize.fminbound(ellipticity, 0.6, 1.0, full_output=0, \
xtol=tol)
print 'ellipticity = %1.3f'%ellip
return ellip
def do_fit(rcoord, od_prof, od_cutoff, guess, pixcal, fitfunc='idealfermi', T=None):
"""Fits an absorption image with an ideal Fermi gas profile
**Inputs**
* rcoord: 1D array containing the radial coordinate
* od_prof: 1D array containing the radially averaged OD profile
* od_cutoff: int, the index of rcoord from where the fit has to be
performed.
* guess: tuple, initial fit parameters, the three elements are n0, a,
bprime
* pixcal: float, pixel size calibration in m/pix.
**Outputs**
* ToverTF: float, the temperature of the Fermi gas in units of T_F
* N: float, the number of atoms of the Fermi gas
* ans: tuple, containing the fit result
**Optional inputs**
* fitfunc: string, name of the fit function to be used. Valid choices are
idealfermi, gaussian, idealfermi_fixedT
* T: float, the temperature for idealfermi_fixedT
"""
def fit_idealfermi():
ans = fit1dfunc(ideal_fermi_radial, rcoord[od_cutoff:], \
od_prof[od_cutoff:], guess)#, \
#weights=np.sqrt(rcoord[od_cutoff:]))
# compute temperature and number of atoms and print result
ToverTF, N = ideal_fermi_numbers(ans, pixcal)
print 'T/T_F = %1.5f'%(ToverTF[0])
print 'N = %1.1f million \n'%(N*1e-6)
return ans, ToverTF, N
def fit_idealfermi_fixedT():
# this is used by texreport.py
logfugacity = fugacity_from_temp(T)
residuals = lambda p, rr, rrdata: ideal_fermi_radial(rr, p[0], \
logfugacity, p[1]) - rrdata
ans, cov_x, infodict, mesg, success = sp.optimize.leastsq(residuals, \
[guess[0], guess[2]], args=(rcoord[od_cutoff:], od_prof[od_cutoff:])\
, full_output=True)
ToverTF = 0 # fix later
N = 0 # fix later
ans = (ans[0], logfugacity, ans[1])
return ans, ToverTF, N
def fit_gaussian():
ans = fit1dfunc(gaussian, rcoord[od_cutoff:], \
od_prof[od_cutoff:], [guess[0], guess[2]])
ToverTF = 1e3 # fix later
N = gaussian_numbers(ans, pixcal)
return ans, ToverTF, N
if fitfunc=='idealfermi':
ans, ToverTF, N = fit_idealfermi()
elif fitfunc=='idealfermi_err':
ans, ToverTF, N = fit_idealfermi()
T = ToverTF + 0.03
ans_plus = fit_idealfermi_fixedT()[0]
T = max(0.001, ToverTF - 0.03)
ans_min = fit_idealfermi_fixedT()[0]
return ToverTF, N, [ans, ans_plus, ans_min]
elif fitfunc=='idealfermi_fixedT':
ans, ToverTF, N = fit_idealfermi_fixedT()
elif fitfunc=='gaussian':
ans, ToverTF, N = fit_gaussian()
else:
print 'Fitfunc has an unknown value'
return ToverTF, N, ans
def fit_img(transimg, odmax=1., showfig=True, elliptic=None, pixcal=10e-6,
fitfunc='idealfermi', T=None, full_output=None, norm=True):
"""Fits an absorption image with an ideal Fermi gas profile
The image is normalized, then azimuthally averaged, then fitted. If the
input is a list of imaged they are separately normalized and then averaged
and fitted.
**Inputs**
* transimg: 2D array or list of 2D arrays, containing the image data
**Outputs**
* ToverTF: float, the temperature of the Fermi gas in units of T_F
* N: float, the number of atoms of the Fermi gas
**Optional inputs**
* od_max = float, the maximum optical density that is used in the fit
* showfig: boolean, determines if a figure is shown with density profile
and fit
* elliptic: tuple, containing two elements. the first one is the
ellipticity (or ratio of major and minor axes), the second one is the
angle by which the major axis is rotated from the y-axis
* pixcal: float, pixel size calibration in m/pix.
* fitfunc: string, name of the fit function to be used. Valid choices are
idealfermi, gaussian, idealfermi_fixedT
* T: float, the temperature for idealfermi_fixedT
* full_output: string, if value is ``odysseus`` the correct objects for
the Odysseus GUI are returned
* norm: bool, if False the normalization of the image is turned off.
This is mainly useful if you fit computer-generated images or
images that you already normalized some other way.
"""
# if the input is a list of images, they are separately normalized and
# then averaged here. also, the ellipticity of the averaged image is found
if isinstance(transimg, list):
avgimg = np.zeros(transimg[0].shape)
for img in transimg:
avgimg += norm_and_guess(img)[0]
transimg = avgimg/np.float(len(transimg))
if elliptic:
elliptic = (find_ellipticity(transimg), 0)
else:
pass
if norm:
transimg, odimg, com, n0, a, bprime = norm_and_guess(transimg)
else:
transimg, odimg, com, n0, a, bprime = norm_and_guess(transimg, norm=False)
# radial averaging of transmission image
rcoord, rtrans_prof = radial_interpolate(transimg, com, 0.3,
elliptic=elliptic)
# generate radial density profile
od_prof = trans2od(rtrans_prof)
# determine which part of od_prof to fit, to reduce effect
# of saturation of OD
od_cutoff = find_fitrange(od_prof, odmax)
# do the fit
guess = (n0, a, bprime)
ToverTF, N, ans = do_fit(rcoord, od_prof, od_cutoff, guess, pixcal,
fitfunc=fitfunc, T=T)
# plot results
if showfig:
fit_prof = ideal_fermi_radial(rcoord, *ans)
fig = show_fitresult(rcoord, od_prof, fit_prof, ToverTF, N, showfig=True)
if not full_output:
return (ToverTF, N, ans)
elif full_output=='odysseus':
if fitfunc=='idealfermi':
fit_prof = ideal_fermi_radial(rcoord, *ans)
elif fitfunc=='idealfermi_err':
fit_prof = ideal_fermi_radial(rcoord, *ans[0])
errprof_plus = ideal_fermi_radial(rcoord, *ans[1])
errprof_min = ideal_fermi_radial(rcoord, *ans[2])
return (ToverTF, N, com, ans, rcoord, od_prof, fit_prof,
errprof_plus, errprof_min)
elif fitfunc=='idealfermi_fixedT':
fit_prof = ideal_fermi_radial(rcoord, *ans)
elif fitfunc=='gaussian':
# T is high (inf) for Gaussian, put it back in to not mess up GUI
#ans = (ans[0], 4., ans[1])
fit_prof = gaussian(rcoord, *ans)
ans = (ans[0], 4., ans[1])
else:
print 'fitfunc has the wrong value'
raise ValueError
return (ToverTF, N, com, ans, rcoord, od_prof, fit_prof)
else:
print 'Unknown input value for full_output!\n'
raise ValueError