qcodespp 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qcodespp/__init__.py +77 -0
- qcodespp/actions.py +237 -0
- qcodespp/data/__init__.py +0 -0
- qcodespp/data/data_array.py +555 -0
- qcodespp/data/data_set.py +926 -0
- qcodespp/data/format.py +338 -0
- qcodespp/data/gnuplot_format.py +425 -0
- qcodespp/data/hdf5_format.py +537 -0
- qcodespp/data/hdf5_format_hickle.py +63 -0
- qcodespp/data/io.py +241 -0
- qcodespp/data/location.py +171 -0
- qcodespp/instrument_drivers/Keysight/Keysight_34450A.py +9 -0
- qcodespp/instrument_drivers/Keysight/__init__.py +0 -0
- qcodespp/instrument_drivers/Keysight/private/Keysight_344xxA.py +456 -0
- qcodespp/instrument_drivers/Keysight/private/__init__.py +0 -0
- qcodespp/instrument_drivers/Lakeshore/Model_340.py +483 -0
- qcodespp/instrument_drivers/Lakeshore/__init__.py +0 -0
- qcodespp/instrument_drivers/QDevil/159fit_params_high_latest.json +386 -0
- qcodespp/instrument_drivers/QDevil/159fit_params_low_latest.json +386 -0
- qcodespp/instrument_drivers/QDevil/QDAC2.py +3229 -0
- qcodespp/instrument_drivers/QDevil/QDAC2_Array.py +300 -0
- qcodespp/instrument_drivers/QDevil/QSwitch_elab.py +652 -0
- qcodespp/instrument_drivers/QDevil/__init__.py +0 -0
- qcodespp/instrument_drivers/ZI/ZIHF2LI.py +415 -0
- qcodespp/instrument_drivers/ZI/ZIMFLI.py +764 -0
- qcodespp/instrument_drivers/ZI/ZIMFLI_sync.py +108 -0
- qcodespp/instrument_drivers/ZI/ZIUHFLI.py +2179 -0
- qcodespp/instrument_drivers/ZI/__init__.py +1 -0
- qcodespp/instrument_drivers/__init__.py +0 -0
- qcodespp/instrument_drivers/cryogenic/CryogenicSMS120C.py +599 -0
- qcodespp/instrument_drivers/cryogenic/__init__.py +0 -0
- qcodespp/instrument_drivers/instrument_mocks.py +379 -0
- qcodespp/instrument_drivers/multiplexer_instrument.py +287 -0
- qcodespp/instrument_drivers/national_instruments/PXI_4461.py +451 -0
- qcodespp/instrument_drivers/national_instruments/PXI_4462.py +290 -0
- qcodespp/instrument_drivers/national_instruments/PXI_6259.py +436 -0
- qcodespp/instrument_drivers/national_instruments/PXI_6733.py +230 -0
- qcodespp/instrument_drivers/national_instruments/PXI_6733_e.py +243 -0
- qcodespp/instrument_drivers/national_instruments/__init__.py +0 -0
- qcodespp/instrument_drivers/oxford/__init__.py +0 -0
- qcodespp/instrument_drivers/oxford/mercuryiPS_serial.py +658 -0
- qcodespp/instrument_drivers/oxford/triton.py +434 -0
- qcodespp/instrument_drivers/tektronix/Keithley_2000_Scan.py +65 -0
- qcodespp/instrument_drivers/tektronix/Keithley_2182a.py +190 -0
- qcodespp/instrument_drivers/tektronix/Keithley_2400.py +158 -0
- qcodespp/instrument_drivers/tektronix/Keithley_2450.py +242 -0
- qcodespp/instrument_drivers/tektronix/Keithley_2700.py +397 -0
- qcodespp/instrument_drivers/tektronix/Keithley_6500.py +161 -0
- qcodespp/instrument_drivers/tektronix/__init__.py +0 -0
- qcodespp/instrument_drivers/yokogawa/GS200.py +513 -0
- qcodespp/instrument_drivers/yokogawa/__init__.py +0 -0
- qcodespp/loops.py +1342 -0
- qcodespp/measure.py +310 -0
- qcodespp/parameters.py +311 -0
- qcodespp/plotting/RemotePlot.py +469 -0
- qcodespp/plotting/RemotePlotClient.py +367 -0
- qcodespp/plotting/RemoteQtPlotWidgets.py +871 -0
- qcodespp/plotting/__init__.py +0 -0
- qcodespp/plotting/analysis_tools.py +195 -0
- qcodespp/plotting/colors.py +135 -0
- qcodespp/plotting/offline/__init__.py +0 -0
- qcodespp/plotting/offline/datatypes.py +999 -0
- qcodespp/plotting/offline/design.py +906 -0
- qcodespp/plotting/offline/design.ui +1578 -0
- qcodespp/plotting/offline/editor.py +2966 -0
- qcodespp/plotting/offline/filters.py +561 -0
- qcodespp/plotting/offline/fits.py +1171 -0
- qcodespp/plotting/offline/helpers.py +288 -0
- qcodespp/plotting/offline/iconGadget.png +0 -0
- qcodespp/plotting/offline/main.py +55 -0
- qcodespp/plotting/offline/popupwindows.py +1848 -0
- qcodespp/plotting/offline/qcodes_pp_extension.py +328 -0
- qcodespp/plotting/offline/sidebars.py +1108 -0
- qcodespp/station.py +290 -0
- qcodespp/utils/__init__.py +0 -0
- qcodespp/utils/helpers.py +618 -0
- qcodespp/utils/visa_helpers.py +61 -0
- qcodespp/version.py +12 -0
- qcodespp-0.1.0.dist-info/METADATA +91 -0
- qcodespp-0.1.0.dist-info/RECORD +82 -0
- qcodespp-0.1.0.dist-info/WHEEL +4 -0
- qcodespp-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,1171 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from lmfit import models as lmm
|
|
3
|
+
from lmfit import Model
|
|
4
|
+
from functools import partial
|
|
5
|
+
|
|
6
|
+
# Fit functions
|
|
7
|
+
|
|
8
|
+
# Polynomials and powers
|
|
9
|
+
def linear(xdata,ydata,p0=None,inputinfo=None):
|
|
10
|
+
"""
|
|
11
|
+
Fit a linear model to the data.
|
|
12
|
+
Args:
|
|
13
|
+
xdata: x data to fit
|
|
14
|
+
ydata: y data to fit
|
|
15
|
+
|
|
16
|
+
Initial guesses and inputinfo not used.
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
result: lmfit result object.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
model=lmm.LinearModel()
|
|
23
|
+
params=model.guess(ydata, x=xdata)
|
|
24
|
+
result = model.fit(ydata, params, x=xdata)
|
|
25
|
+
return result
|
|
26
|
+
|
|
27
|
+
def polynomial(xdata,ydata,p0=None,inputinfo=2):
|
|
28
|
+
"""Fit a polynomial model to the data.
|
|
29
|
+
Args:
|
|
30
|
+
xdata: x data to fit
|
|
31
|
+
ydata: y data to fit
|
|
32
|
+
inputinfo (int): degree of the polynomial to fit. Default 2
|
|
33
|
+
|
|
34
|
+
Initial guesses (p0) are not used.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
result: lmfit result object.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
degree = int(inputinfo[0]) or int(inputinfo)
|
|
41
|
+
model=lmm.PolynomialModel(degree=degree)
|
|
42
|
+
params=model.guess(ydata, x=xdata)
|
|
43
|
+
result = model.fit(ydata, params, x=xdata)
|
|
44
|
+
return result
|
|
45
|
+
|
|
46
|
+
def fit_powerlaw(xdata,ydata, p0=None,inputinfo=[1,0]):
|
|
47
|
+
"""
|
|
48
|
+
Fit a power law model to the data.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
xdata: x data to fit
|
|
52
|
+
ydata: y data to fit
|
|
53
|
+
p0 (opt.): initial guesses for the parameters. Should be a list of floats.
|
|
54
|
+
inputinfo: a list containing the number of terms in the power law and whether to include a constant offset.
|
|
55
|
+
Returns:
|
|
56
|
+
result: lmfit result object.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
order=inputinfo[0]
|
|
60
|
+
constant=inputinfo[1]
|
|
61
|
+
|
|
62
|
+
model= lmm.PowerLawModel(prefix='power0_')
|
|
63
|
+
params = model.make_params()
|
|
64
|
+
for i in range(int(order-1)):
|
|
65
|
+
newmodel = lmm.PowerLawModel(prefix=f'power{i+1}_')
|
|
66
|
+
pars = newmodel.make_params()
|
|
67
|
+
model = model + newmodel
|
|
68
|
+
params.update(pars)
|
|
69
|
+
if constant !=0:
|
|
70
|
+
bg = lmm.ConstantModel(prefix='bg_')
|
|
71
|
+
pars = bg.make_params()
|
|
72
|
+
pars['bg_c'].set(constant)
|
|
73
|
+
model = model + bg
|
|
74
|
+
params.update(pars)
|
|
75
|
+
|
|
76
|
+
if p0:
|
|
77
|
+
for i,key in enumerate(params.keys()):
|
|
78
|
+
params[key].set(float(p0[i]))
|
|
79
|
+
else:
|
|
80
|
+
for key in params.keys():
|
|
81
|
+
if 'amplitude' in key:
|
|
82
|
+
params[key].set(ydata.max()-ydata.min())
|
|
83
|
+
elif 'expo' in key:
|
|
84
|
+
params[key].set(1)
|
|
85
|
+
|
|
86
|
+
result = model.fit(ydata, params, x=xdata)
|
|
87
|
+
return result
|
|
88
|
+
|
|
89
|
+
def fit_exponentials(xdata,ydata, p0=None,inputinfo=[1,0]):
|
|
90
|
+
"""
|
|
91
|
+
Fit one or more exponential terms to the data, with or without a constant offset.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
xdata: x data to fit
|
|
95
|
+
ydata: y data to fit
|
|
96
|
+
p0 (opt.): initial guesses for the parameters. Should be a list of floats.
|
|
97
|
+
inputinfo: a list containing the number of terms in the exponential and whether to include a constant offset.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
result: lmfit result object.
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
order=inputinfo[0]
|
|
104
|
+
constant=inputinfo[1]
|
|
105
|
+
|
|
106
|
+
model= lmm.ExponentialModel(prefix='term0_')
|
|
107
|
+
params = model.make_params()
|
|
108
|
+
for i in range(int(order-1)):
|
|
109
|
+
newmodel = lmm.ExponentialModel(prefix=f'term{i+1}_')
|
|
110
|
+
pars = newmodel.make_params()
|
|
111
|
+
model = model + newmodel
|
|
112
|
+
params.update(pars)
|
|
113
|
+
if constant !=0:
|
|
114
|
+
bg = lmm.ConstantModel(prefix='bg_')
|
|
115
|
+
pars = bg.make_params()
|
|
116
|
+
pars['bg_c'].set(constant)
|
|
117
|
+
model = model + bg
|
|
118
|
+
params.update(pars)
|
|
119
|
+
|
|
120
|
+
if p0:
|
|
121
|
+
for i,key in enumerate(params.keys()):
|
|
122
|
+
params[key].set(float(p0[i]))
|
|
123
|
+
|
|
124
|
+
else:
|
|
125
|
+
amplitudes=ydata.max()-ydata.min()
|
|
126
|
+
decays=xdata.max()-xdata.min()
|
|
127
|
+
#more or less. Beats negative infinity.
|
|
128
|
+
for key in params.keys():
|
|
129
|
+
if 'amplitude' in key:
|
|
130
|
+
params[key].set(amplitudes)
|
|
131
|
+
elif 'decay' in key:
|
|
132
|
+
params[key].set(decays)
|
|
133
|
+
|
|
134
|
+
result = model.fit(ydata, params, x=xdata)
|
|
135
|
+
return result
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
#Peak fitting
|
|
139
|
+
def fit_lorgausstype(modeltype,xdata,ydata,p0=None,inputinfo=[1,0]):
|
|
140
|
+
'''
|
|
141
|
+
Fits x,y data with peaks characterised by amplitude, fwhm and position.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
modeltype: lmfit model to use for fitting. Options are:
|
|
145
|
+
LorentzianModel, GaussianModel, LognormalModel, StudentsTModel, DampedOscillatorModel
|
|
146
|
+
xdata: x data to fit
|
|
147
|
+
ydata: y data to fit
|
|
148
|
+
p0 (opt.): initial guesses for the parameters. Should be a list of strings.
|
|
149
|
+
inputinfo: a list containing the number of peaks to fit and whether to include a constant offset.
|
|
150
|
+
Returns:
|
|
151
|
+
result: lmfit result object.
|
|
152
|
+
'''
|
|
153
|
+
|
|
154
|
+
numofpeaks=inputinfo[0]
|
|
155
|
+
usebackground=inputinfo[1]
|
|
156
|
+
sigmas=None
|
|
157
|
+
amplitudes=None
|
|
158
|
+
|
|
159
|
+
# if xdata[0]>xdata[-1]:
|
|
160
|
+
# xdata=xdata[::-1]
|
|
161
|
+
# ydata=ydata[::-1]
|
|
162
|
+
|
|
163
|
+
peakspacing=(xdata.max()-xdata.min())/numofpeaks
|
|
164
|
+
rough_peak_positions=[i*peakspacing+peakspacing/2+xdata.min() for i in range(int(numofpeaks))]
|
|
165
|
+
|
|
166
|
+
if p0: #Format should be list of strings: ['w w ... w','a a ... a','x x ... x','c']
|
|
167
|
+
sigmas=[float(par) for par in p0[0].split()]
|
|
168
|
+
amplitudes=[float(par) for par in p0[1].split()]
|
|
169
|
+
rough_peak_positions=[float(par) for par in p0[2].split()]
|
|
170
|
+
if usebackground==1:
|
|
171
|
+
try:
|
|
172
|
+
background=p0[3]
|
|
173
|
+
except:
|
|
174
|
+
background=0
|
|
175
|
+
else:
|
|
176
|
+
background=None
|
|
177
|
+
|
|
178
|
+
if sigmas==None: #Sigma will be much smaller than the peak spacing unless peaks are overlapping. However, starting with a low value seems to work even if overlapping, but not the other way around.
|
|
179
|
+
sigmas=[np.abs(xdata[-1]-xdata[0])/(12*numofpeaks) for i in range(int(numofpeaks))]
|
|
180
|
+
if amplitudes ==None: #Guess that the height will be close to the maximum value of the data: calculate amplitude based on gaussian.
|
|
181
|
+
height=ydata.max()-ydata.min()
|
|
182
|
+
amp=height*sigmas[0]*np.sqrt(2*np.pi)
|
|
183
|
+
# but to be honest i get better results just using the height...doesn't make sense but whatever
|
|
184
|
+
amplitudes=[height for i in range(int(numofpeaks))]
|
|
185
|
+
|
|
186
|
+
peakpos0=rough_peak_positions[0]
|
|
187
|
+
peakpositions=rough_peak_positions[1:]
|
|
188
|
+
|
|
189
|
+
def add_peak(prefix, center, amplitude, sigma):
|
|
190
|
+
peak = modeltype(prefix=prefix)
|
|
191
|
+
pars = peak.make_params()
|
|
192
|
+
pars[prefix + 'center'].set(center)
|
|
193
|
+
pars[prefix + 'amplitude'].set(amplitude)
|
|
194
|
+
pars[prefix + 'sigma'].set(sigma, min=0)
|
|
195
|
+
return peak, pars
|
|
196
|
+
|
|
197
|
+
model = modeltype(prefix='peak0_')
|
|
198
|
+
params = model.make_params()
|
|
199
|
+
params['peak0_center'].set(peakpos0)
|
|
200
|
+
params['peak0_amplitude'].set(amplitudes[0])
|
|
201
|
+
params['peak0_sigma'].set(sigmas[0], min=0)
|
|
202
|
+
|
|
203
|
+
for i, cen in enumerate(peakpositions):
|
|
204
|
+
peak, pars = add_peak('peak%d_' % (i+1), cen, amplitudes[i+1], sigmas[i+1])
|
|
205
|
+
model = model + peak
|
|
206
|
+
params.update(pars)
|
|
207
|
+
|
|
208
|
+
if background is not None:
|
|
209
|
+
bg = lmm.ConstantModel(prefix='bg_')
|
|
210
|
+
pars = bg.make_params()
|
|
211
|
+
pars['bg_c'].set(background)
|
|
212
|
+
model = model + bg
|
|
213
|
+
params.update(pars)
|
|
214
|
+
|
|
215
|
+
result = model.fit(ydata, params, x=xdata)
|
|
216
|
+
return result
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
# Function for lmfit peaks with four parameters.
|
|
220
|
+
def fit_voigttype(modeltype,xdata,ydata,p0=None,inputinfo=[1,0]):
|
|
221
|
+
"""
|
|
222
|
+
Fits x,y data with peaks characterised by amplitude, fwhm, position and gamma.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
modeltype: lmfit model to use for fitting. Options are:
|
|
226
|
+
VoigtModel, PseudoVoigtModel, BreitWignerModel, SplitLorentzianModel, ExponentialGaussianModel,
|
|
227
|
+
SkewedGaussianModel, MoffatModel, Pearson7Model, DampedHarmonicOscillatorModel, DoniachModel
|
|
228
|
+
xdata: x data to fit
|
|
229
|
+
ydata: y data to fit
|
|
230
|
+
p0 (opt.): initial guesses for the parameters. Should be a list of strings.
|
|
231
|
+
inputinfo: a list containing the number of peaks to fit and whether to include a constant offset.
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
result: lmfit result object.
|
|
235
|
+
"""
|
|
236
|
+
fourthparamdict={'SplitLorentzianModel':'sigma_r',
|
|
237
|
+
'PseudoVoigtModel':'fraction',
|
|
238
|
+
'MoffatModel':'beta',
|
|
239
|
+
'Pearson7Model':'expon',
|
|
240
|
+
'BreitWignerModel':'q'}
|
|
241
|
+
if modeltype.__name__ in fourthparamdict.keys():
|
|
242
|
+
fourthparam=fourthparamdict[modeltype.__name__]
|
|
243
|
+
else:
|
|
244
|
+
fourthparam='gamma'
|
|
245
|
+
|
|
246
|
+
numofpeaks=inputinfo[0]
|
|
247
|
+
usebackground=inputinfo[1]
|
|
248
|
+
sigmas=None
|
|
249
|
+
amplitudes=None
|
|
250
|
+
gammas=None
|
|
251
|
+
|
|
252
|
+
# if xdata[0]>xdata[-1]:
|
|
253
|
+
# xdata=xdata[::-1]
|
|
254
|
+
# ydata=ydata[::-1]
|
|
255
|
+
# flipped=True
|
|
256
|
+
# else:
|
|
257
|
+
# flipped=False
|
|
258
|
+
|
|
259
|
+
peakspacing=(xdata.max()-xdata.min())/numofpeaks
|
|
260
|
+
rough_peak_positions=[i*peakspacing+peakspacing/2+xdata.min() for i in range(int(numofpeaks))]
|
|
261
|
+
|
|
262
|
+
if p0: #Format should be list of strings: ['w w ... w','a a ... a','x x ... x','c']
|
|
263
|
+
sigmas=[float(par) for par in p0[0].split()]
|
|
264
|
+
amplitudes=[float(par) for par in p0[1].split()]
|
|
265
|
+
rough_peak_positions=[float(par) for par in p0[2].split()]
|
|
266
|
+
gammas=[float(par) for par in p0[3].split()]
|
|
267
|
+
if usebackground==1:
|
|
268
|
+
try:
|
|
269
|
+
background=p0[4]
|
|
270
|
+
except:
|
|
271
|
+
background=0
|
|
272
|
+
else:
|
|
273
|
+
background=None
|
|
274
|
+
|
|
275
|
+
if amplitudes ==None: #Guess that the amplitudes will be close to the maximum value of the data
|
|
276
|
+
amplitudes=[ydata.max()-ydata.min() for i in range(int(numofpeaks))]
|
|
277
|
+
if sigmas==None: #Sigma will be much smaller than the peak spacing unless peaks are overlapping. However, starting with a low value seems to work even if overlapping, but not the other way around.
|
|
278
|
+
sigmas=[np.abs(xdata[-1]-xdata[0])/(12*numofpeaks) for i in range(int(numofpeaks))]
|
|
279
|
+
if gammas==None: #God I have no idea
|
|
280
|
+
gammas=[0 for i in range(int(numofpeaks))]
|
|
281
|
+
|
|
282
|
+
peakpos0=rough_peak_positions[0]
|
|
283
|
+
peakpositions=rough_peak_positions[1:]
|
|
284
|
+
|
|
285
|
+
def add_peak(prefix, center, amplitude, sigma,gamma):
|
|
286
|
+
peak = modeltype(prefix=prefix)
|
|
287
|
+
pars = peak.make_params()
|
|
288
|
+
pars[prefix + 'center'].set(center)
|
|
289
|
+
pars[prefix + 'amplitude'].set(amplitude)
|
|
290
|
+
pars[prefix + 'sigma'].set(sigma, min=0)
|
|
291
|
+
pars[prefix + fourthparam].set(gamma)
|
|
292
|
+
return peak, pars
|
|
293
|
+
|
|
294
|
+
model = modeltype(prefix='peak0_')
|
|
295
|
+
params = model.make_params()
|
|
296
|
+
params['peak0_center'].set(peakpos0)
|
|
297
|
+
params['peak0_amplitude'].set(amplitudes[0])
|
|
298
|
+
params['peak0_sigma'].set(sigmas[0], min=0)
|
|
299
|
+
params['peak0_'+fourthparam].set(gammas[0])
|
|
300
|
+
|
|
301
|
+
for i, cen in enumerate(peakpositions):
|
|
302
|
+
peak, pars = add_peak('peak%d_' % (i+1), cen, amplitudes[i+1], sigmas[i+1], gammas[i+1])
|
|
303
|
+
model = model + peak
|
|
304
|
+
params.update(pars)
|
|
305
|
+
|
|
306
|
+
if background is not None:
|
|
307
|
+
bg = lmm.ConstantModel(prefix='bg_')
|
|
308
|
+
pars = bg.make_params()
|
|
309
|
+
pars['bg_c'].set(background)
|
|
310
|
+
model = model + bg
|
|
311
|
+
params.update(pars)
|
|
312
|
+
|
|
313
|
+
result = model.fit(ydata, params, x=xdata)
|
|
314
|
+
return result
|
|
315
|
+
|
|
316
|
+
# Function for lmfit peaks with five parameters.
|
|
317
|
+
def fit_skewedpeaks(modeltype,xdata,ydata,p0=None,inputinfo=[1,0]):
|
|
318
|
+
"""
|
|
319
|
+
Fits x,y data with peaks characterised by amplitude, fwhm, position, gamma and skew.
|
|
320
|
+
Args:
|
|
321
|
+
modeltype: lmfit model to use for fitting. Options are:
|
|
322
|
+
Pearson4Model, SkewedVoigtModel
|
|
323
|
+
xdata: x data to fit
|
|
324
|
+
ydata: y data to fit
|
|
325
|
+
p0 (opt.): initial guesses for the parameters. Should be a list of strings.
|
|
326
|
+
inputinfo: a list containing the number of peaks to fit and whether to include a constant offset.
|
|
327
|
+
Returns:
|
|
328
|
+
result: lmfit result object.
|
|
329
|
+
"""
|
|
330
|
+
fourthparamdict={'Pearson4Model':'expon'}
|
|
331
|
+
if modeltype.__name__ in fourthparamdict.keys():
|
|
332
|
+
fourthparam=fourthparamdict[modeltype.__name__]
|
|
333
|
+
else:
|
|
334
|
+
fourthparam='gamma'
|
|
335
|
+
#Fifth parameter is always 'skew'
|
|
336
|
+
|
|
337
|
+
numofpeaks=inputinfo[0]
|
|
338
|
+
usebackground=inputinfo[1]
|
|
339
|
+
sigmas=None
|
|
340
|
+
amplitudes=None
|
|
341
|
+
gammas=None
|
|
342
|
+
skews=None
|
|
343
|
+
|
|
344
|
+
# if xdata[0]>xdata[-1]:
|
|
345
|
+
# xdata=xdata[::-1]
|
|
346
|
+
# ydata=ydata[::-1]
|
|
347
|
+
|
|
348
|
+
peakspacing=(xdata.max()-xdata.min())/numofpeaks
|
|
349
|
+
rough_peak_positions=[i*peakspacing+peakspacing/2+xdata.min() for i in range(int(numofpeaks))]
|
|
350
|
+
|
|
351
|
+
if p0: #Format should be list of strings: ['w w ... w','a a ... a','x x ... x','c']
|
|
352
|
+
sigmas=[float(par) for par in p0[0].split()]
|
|
353
|
+
amplitudes=[float(par) for par in p0[1].split()]
|
|
354
|
+
rough_peak_positions=[float(par) for par in p0[2].split()]
|
|
355
|
+
gammas=[float(par) for par in p0[3].split()]
|
|
356
|
+
skews=[float(par) for par in p0[4].split()]
|
|
357
|
+
if usebackground==1:
|
|
358
|
+
try:
|
|
359
|
+
background=p0[5]
|
|
360
|
+
except:
|
|
361
|
+
background=0
|
|
362
|
+
else:
|
|
363
|
+
background=None
|
|
364
|
+
|
|
365
|
+
if amplitudes ==None: #Guess that the amplitudes will be close to the maximum value of the data
|
|
366
|
+
amplitudes=[ydata.max()-ydata.min() for i in range(int(numofpeaks))]
|
|
367
|
+
if sigmas==None: #Sigma will be much smaller than the peak spacing unless peaks are overlapping. However, starting with a low value seems to work even if overlapping, but not the other way around.
|
|
368
|
+
sigmas=[np.abs(xdata[-1]-xdata[0])/(12*numofpeaks) for i in range(int(numofpeaks))]
|
|
369
|
+
if gammas==None:
|
|
370
|
+
gammas=[0 for i in range(int(numofpeaks))]
|
|
371
|
+
if skews==None:
|
|
372
|
+
skews=[1 for i in range(int(numofpeaks))]
|
|
373
|
+
|
|
374
|
+
peakpos0=rough_peak_positions[0]
|
|
375
|
+
peakpositions=rough_peak_positions[1:]
|
|
376
|
+
|
|
377
|
+
def add_peak(prefix, center, amplitude, sigma,gamma,skew):
|
|
378
|
+
peak = modeltype(prefix=prefix)
|
|
379
|
+
pars = peak.make_params()
|
|
380
|
+
pars[prefix + 'center'].set(center)
|
|
381
|
+
pars[prefix + 'amplitude'].set(amplitude)
|
|
382
|
+
pars[prefix + 'sigma'].set(sigma, min=0)
|
|
383
|
+
pars[prefix + fourthparam].set(gamma)
|
|
384
|
+
pars[prefix + 'skew'].set(skew)
|
|
385
|
+
return peak, pars
|
|
386
|
+
|
|
387
|
+
model = modeltype(prefix='peak0_')
|
|
388
|
+
params = model.make_params()
|
|
389
|
+
params['peak0_center'].set(peakpos0)
|
|
390
|
+
params['peak0_amplitude'].set(amplitudes[0])
|
|
391
|
+
params['peak0_sigma'].set(sigmas[0], min=0)
|
|
392
|
+
params['peak0_'+fourthparam].set(gammas[0])
|
|
393
|
+
params['peak0_skew'].set(skews[0])
|
|
394
|
+
|
|
395
|
+
for i, cen in enumerate(peakpositions):
|
|
396
|
+
peak, pars = add_peak('peak%d_' % (i+1), cen, amplitudes[i+1], sigmas[i+1], gammas[i+1],skews[i+1])
|
|
397
|
+
model = model + peak
|
|
398
|
+
params.update(pars)
|
|
399
|
+
|
|
400
|
+
if background is not None:
|
|
401
|
+
bg = lmm.ConstantModel(prefix='bg_')
|
|
402
|
+
pars = bg.make_params()
|
|
403
|
+
pars['bg_c'].set(background)
|
|
404
|
+
model = model + bg
|
|
405
|
+
params.update(pars)
|
|
406
|
+
|
|
407
|
+
result = model.fit(ydata, params, x=xdata)
|
|
408
|
+
return result
|
|
409
|
+
|
|
410
|
+
#Multiple sine wave fitting
|
|
411
|
+
def fit_sines(xdata,ydata,p0=None,inputinfo=[1,0]):
|
|
412
|
+
"""
|
|
413
|
+
Fits x,y data with multiple sine waves characterised by amplitude, frequency, phase and position.
|
|
414
|
+
Args:
|
|
415
|
+
xdata: x data to fit
|
|
416
|
+
ydata: y data to fit
|
|
417
|
+
p0 (opt.): initial guesses for the parameters. Should be a list of strings.
|
|
418
|
+
inputinfo: a list containing the number of sine waves to fit and whether to include a constant offset.
|
|
419
|
+
Returns:
|
|
420
|
+
result: lmfit result object.
|
|
421
|
+
"""
|
|
422
|
+
|
|
423
|
+
modeltype=lmm.SineModel
|
|
424
|
+
#Fits x,y data with peaks characterised by amplitude, fwhm and position.
|
|
425
|
+
#Custom starting amplitudes (proportional to height) and sigmas (proportional to width) may also be given
|
|
426
|
+
#Returns the entire lmfit result.
|
|
427
|
+
numofwaves=inputinfo[0]
|
|
428
|
+
usebackground=inputinfo[1]
|
|
429
|
+
amps=None
|
|
430
|
+
freqs=None
|
|
431
|
+
phases=None
|
|
432
|
+
|
|
433
|
+
# if xdata[0]>xdata[-1]:
|
|
434
|
+
# xdata=xdata[::-1]
|
|
435
|
+
# ydata=ydata[::-1]
|
|
436
|
+
|
|
437
|
+
if p0: #Format should be list of strings: ['A A ... A','f f ... f','ph ph ... ph','c']
|
|
438
|
+
amps=[float(par) for par in p0[0].split()]
|
|
439
|
+
freqs=[float(par) for par in p0[1].split()]
|
|
440
|
+
phases=[float(par) for par in p0[2].split()]
|
|
441
|
+
if usebackground==1:
|
|
442
|
+
try:
|
|
443
|
+
background=p0[3]
|
|
444
|
+
except:
|
|
445
|
+
background=0
|
|
446
|
+
else:
|
|
447
|
+
background=None
|
|
448
|
+
|
|
449
|
+
if amps ==None: #Guess that the amplitudes will be close to the maximum minus minimum
|
|
450
|
+
amps=[ydata.max()-ydata.min() for i in range(int(numofwaves))]
|
|
451
|
+
if freqs==None: #Guess that the user probably provides somewhere between 1 and 20 periods for fitting.
|
|
452
|
+
freqs=[10/np.abs(xdata[-1]-xdata[0]) for i in range(int(numofwaves))]
|
|
453
|
+
if phases==None: # The phases (surely?!) just have to be something sensible in units of radians
|
|
454
|
+
phases=[0.1 for i in range(int(numofwaves))]
|
|
455
|
+
|
|
456
|
+
def add_wave(prefix, amplitude, frequency, phase):
|
|
457
|
+
wave = modeltype(prefix=prefix)
|
|
458
|
+
pars = wave.make_params()
|
|
459
|
+
pars[prefix + 'amplitude'].set(amplitude)
|
|
460
|
+
pars[prefix + 'frequency'].set(frequency)
|
|
461
|
+
pars[prefix + 'shift'].set(phase)
|
|
462
|
+
return wave, pars
|
|
463
|
+
|
|
464
|
+
model = modeltype(prefix='wave0_')
|
|
465
|
+
params = model.make_params()
|
|
466
|
+
params['wave0_amplitude'].set(amps[0])
|
|
467
|
+
params['wave0_frequency'].set(freqs[0])
|
|
468
|
+
params['wave0_shift'].set(phases[0])
|
|
469
|
+
|
|
470
|
+
new_amps=amps[1:]
|
|
471
|
+
|
|
472
|
+
for i, amp in enumerate(new_amps):
|
|
473
|
+
wave, pars = add_wave('wave%d_' % (i+1), amp, freqs[i+1], phases[i+1])
|
|
474
|
+
model = model + wave
|
|
475
|
+
params.update(pars)
|
|
476
|
+
|
|
477
|
+
if background is not None:
|
|
478
|
+
bg = lmm.ConstantModel(prefix='bg_')
|
|
479
|
+
pars = bg.make_params()
|
|
480
|
+
pars['bg_c'].set(background)
|
|
481
|
+
model = model + bg
|
|
482
|
+
params.update(pars)
|
|
483
|
+
|
|
484
|
+
result = model.fit(ydata, params, x=xdata)
|
|
485
|
+
return result
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
#Fit a thermal distribution of MB, FD or BE type.
|
|
489
|
+
def thermal_fit(modeltype,xdata,ydata,p0=None,inputinfo=None):
|
|
490
|
+
"""
|
|
491
|
+
Fits x,y data with a thermal distribution characterised by temperature and amplitude.
|
|
492
|
+
Args:
|
|
493
|
+
modeltype (str): the type of thermal distribution to fit. Options are:
|
|
494
|
+
maxwell, fermi, bose.
|
|
495
|
+
xdata: x data to fit
|
|
496
|
+
ydata: y data to fit
|
|
497
|
+
p0 (opt.): initial guesses for the parameters. Should be a list of floats.
|
|
498
|
+
inputinfo: not used.
|
|
499
|
+
Returns:
|
|
500
|
+
result: lmfit result object.
|
|
501
|
+
"""
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
model=lmm.ThermalDistributionModel(form=modeltype)
|
|
505
|
+
|
|
506
|
+
if p0:
|
|
507
|
+
params=model.make_params()
|
|
508
|
+
for i,key in enumerate(params.keys()):
|
|
509
|
+
params[key]=float(p0[i])
|
|
510
|
+
else:
|
|
511
|
+
params=model.guess(ydata,x=xdata)
|
|
512
|
+
result=model.fit(ydata,params,x=xdata)
|
|
513
|
+
return result
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
#Fit a step function.
|
|
517
|
+
def step_fit(modeltype,xdata,ydata,p0=None,inputinfo=None):
|
|
518
|
+
"""
|
|
519
|
+
Fits x,y data with a step function characterised by amplitude, center and sigma.
|
|
520
|
+
Args:
|
|
521
|
+
modeltype (str): the type of step function to fit. Options are:
|
|
522
|
+
linear, arctan, erf, logistic
|
|
523
|
+
|
|
524
|
+
xdata: x data to fit
|
|
525
|
+
ydata: y data to fit
|
|
526
|
+
p0 (opt.): initial guesses for the parameters. Should be a list of floats.
|
|
527
|
+
inputinfo: not used.
|
|
528
|
+
|
|
529
|
+
Returns:
|
|
530
|
+
result: lmfit result object.
|
|
531
|
+
"""
|
|
532
|
+
# if xdata[0]>xdata[-1]:
|
|
533
|
+
# xdata=xdata[::-1]
|
|
534
|
+
# ydata=ydata[::-1]
|
|
535
|
+
model=lmm.StepModel(form=modeltype)
|
|
536
|
+
params=model.make_params()
|
|
537
|
+
if p0:
|
|
538
|
+
for i,key in enumerate(params.keys()):
|
|
539
|
+
params[key]=float(p0[i])
|
|
540
|
+
else:
|
|
541
|
+
if ydata[-1]>ydata[0]:
|
|
542
|
+
params['amplitude'].set(ydata.max())
|
|
543
|
+
else:
|
|
544
|
+
params['amplitude'].set(ydata.min())
|
|
545
|
+
params['center'].set((xdata[0]+xdata[-1])/2)
|
|
546
|
+
params['sigma'].set((xdata[-1]-xdata[0])/10)
|
|
547
|
+
|
|
548
|
+
result=model.fit(ydata,params,x=xdata)
|
|
549
|
+
return result
|
|
550
|
+
|
|
551
|
+
#Fit a rectangle function.
|
|
552
|
+
def rectangle_fit(modeltype,xdata,ydata,p0=None,inputinfo=None):
|
|
553
|
+
|
|
554
|
+
"""
|
|
555
|
+
Fits x,y data with a rectangle function characterised by amplitude, center1, center2, sigma1 and sigma2.
|
|
556
|
+
Args:
|
|
557
|
+
modeltype (str): the type of rectangle function to fit. Options are:
|
|
558
|
+
linear, arctan, erf, logistic
|
|
559
|
+
xdata: x data to fit
|
|
560
|
+
ydata: y data to fit
|
|
561
|
+
p0 (opt.): initial guesses for the parameters. Should be a list of floats.
|
|
562
|
+
inputinfo: not used.
|
|
563
|
+
Returns:
|
|
564
|
+
result: lmfit result object.
|
|
565
|
+
"""
|
|
566
|
+
# if xdata[0]>xdata[-1]:
|
|
567
|
+
# xdata=xdata[::-1]
|
|
568
|
+
# ydata=ydata[::-1]
|
|
569
|
+
model=lmm.RectangleModel(form=modeltype)
|
|
570
|
+
params=model.make_params()
|
|
571
|
+
if p0:
|
|
572
|
+
for i,key in enumerate(params.keys()):
|
|
573
|
+
params[key]=float(p0[i])
|
|
574
|
+
else:
|
|
575
|
+
if ydata[-1]>ydata[0]:
|
|
576
|
+
params['amplitude'].set(ydata.max())
|
|
577
|
+
else:
|
|
578
|
+
params['amplitude'].set(ydata.min())
|
|
579
|
+
params['center1'].set((3*xdata[0]+xdata[-1])/4)
|
|
580
|
+
params['center2'].set((xdata[0]+3*xdata[-1])/4)
|
|
581
|
+
params['sigma1'].set((xdata[-1]-xdata[0])/20)
|
|
582
|
+
params['sigma2'].set((xdata[-1]-xdata[0])/20)
|
|
583
|
+
|
|
584
|
+
result=model.fit(ydata,params,x=xdata)
|
|
585
|
+
return result
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
# Arbitrary expressions get fitted using the below:
|
|
589
|
+
def expression_fit(xdata,ydata,p0,inputinfo):
|
|
590
|
+
""" Fits x,y data with an arbitrary expression using lmfit's ExpressionModel.
|
|
591
|
+
Args:
|
|
592
|
+
xdata: x data to fit
|
|
593
|
+
ydata: y data to fit
|
|
594
|
+
p0 (opt.): initial guesses for the parameters. Should be a list of strings.
|
|
595
|
+
Format should be: ['x0=x0_value', 'G0=G0_value', ...]
|
|
596
|
+
inputinfo: The expression to fit, as a string. Should be a valid lmfit expression.
|
|
597
|
+
Returns:
|
|
598
|
+
result: lmfit result object.
|
|
599
|
+
"""
|
|
600
|
+
|
|
601
|
+
model=lmm.ExpressionModel(inputinfo)
|
|
602
|
+
params=model.make_params()
|
|
603
|
+
for i,whatever in enumerate(p0):
|
|
604
|
+
params[whatever.split('=')[0].strip()].set(value=float(whatever.split('=')[1]))
|
|
605
|
+
|
|
606
|
+
result=model.fit(ydata,params,x=xdata)
|
|
607
|
+
return result
|
|
608
|
+
|
|
609
|
+
#Custom fits
|
|
610
|
+
def QD_fit(xdata,ydata,p0=None,inputinfo=[1,0.01]):
|
|
611
|
+
"""
|
|
612
|
+
Fits one or more Coulomb blockade peaks in the limit of low tunnel coupling: 'G = G_0 * cosh(e*alpha*(Vg - V_0)/(2*k_B*T))**(-2)
|
|
613
|
+
|
|
614
|
+
Args:
|
|
615
|
+
xdata: x data to fit
|
|
616
|
+
ydata: y data to fit
|
|
617
|
+
p0 (opt.): initial guesses for the parameters. Should be a list of strings.
|
|
618
|
+
Format should be: ['x0 x0 ... x0','G0 G0 ... G0','T']
|
|
619
|
+
inputinfo: a list containing the number of peaks to fit and the alpha parameter.
|
|
620
|
+
Format should be: [numofpeaks, alpha]
|
|
621
|
+
Returns:
|
|
622
|
+
result: lmfit result object.
|
|
623
|
+
"""
|
|
624
|
+
numofpeaks=inputinfo[0]
|
|
625
|
+
alpha=inputinfo[1]
|
|
626
|
+
G0s=None
|
|
627
|
+
|
|
628
|
+
if p0: #Format should be list of strings: ['x0 x0 ... x0','G0 G0 ... G0','T']
|
|
629
|
+
rough_peak_positions=[float(par) for par in p0[0].split()]
|
|
630
|
+
G0s=[float(par) for par in p0[1].split()]
|
|
631
|
+
T=[float(p0[2])]
|
|
632
|
+
else:
|
|
633
|
+
peakspacing=(xdata.max()-xdata.min())/numofpeaks
|
|
634
|
+
rough_peak_positions=[i*peakspacing+peakspacing/2+xdata.min() for i in range(int(numofpeaks))]
|
|
635
|
+
G0s=[ydata.max()-ydata.min() for i in range(int(numofpeaks))]
|
|
636
|
+
T=0.01
|
|
637
|
+
|
|
638
|
+
boltzmann = 1.38064852e-23 # J/K
|
|
639
|
+
def QD_model(x, x0, G0, T):
|
|
640
|
+
return G0 * np.cosh(1.60217663e-19*alpha * (x - x0) / (2 * boltzmann * T))**(-2)
|
|
641
|
+
model=Model(QD_model,prefix='peak0_')
|
|
642
|
+
params=model.make_params()
|
|
643
|
+
|
|
644
|
+
params['peak0_G0'].set(value=G0s[0])
|
|
645
|
+
params['peak0_T'].set(value=T)
|
|
646
|
+
params['peak0_x0'].set(value=rough_peak_positions[0])
|
|
647
|
+
|
|
648
|
+
def add_peak(prefix, x0, G0, T):
|
|
649
|
+
peak = Model(QD_model,prefix=prefix)
|
|
650
|
+
pars = peak.make_params()
|
|
651
|
+
pars[prefix + 'x0'].set(x0)
|
|
652
|
+
pars[prefix + 'G0'].set(G0)
|
|
653
|
+
pars[prefix + 'T'].set(T)
|
|
654
|
+
return peak, pars
|
|
655
|
+
|
|
656
|
+
for i, cen in enumerate(rough_peak_positions[1:]):
|
|
657
|
+
peak, pars = add_peak('peak%d_' % (i+1), cen, G0s[i+1], T)
|
|
658
|
+
model = model + peak
|
|
659
|
+
params.update(pars)
|
|
660
|
+
|
|
661
|
+
result=model.fit(ydata,params,x=xdata)
|
|
662
|
+
return result
|
|
663
|
+
|
|
664
|
+
def FET_mobility(xdata,ydata,p0=None,inputinfo=None):
|
|
665
|
+
"""
|
|
666
|
+
Fits x,y data with a FET mobility model: '1/(R_s + L**2/(C*mu*(x-V_th)))'
|
|
667
|
+
Args:
|
|
668
|
+
xdata: x data to fit
|
|
669
|
+
ydata: y data to fit
|
|
670
|
+
p0 (opt.): List of initial guesses for the parameters.
|
|
671
|
+
Format should be: [mu, V_th, R_s]
|
|
672
|
+
inputinfo: a list containing the capacitance C and device length L.
|
|
673
|
+
Format should be: [C, L]
|
|
674
|
+
Returns:
|
|
675
|
+
result: lmfit result object.
|
|
676
|
+
"""
|
|
677
|
+
|
|
678
|
+
if inputinfo is None or len(inputinfo) < 2:
|
|
679
|
+
raise ValueError("inputinfo must contain values for capacitance and device Length: [C, L]")
|
|
680
|
+
C=float(inputinfo[0])
|
|
681
|
+
L=float(inputinfo[1])
|
|
682
|
+
def FET_model(x, mu, V_th, R_s):
|
|
683
|
+
return 1/(R_s + L**2/(C*mu*(x-V_th)))
|
|
684
|
+
model=Model(FET_model)
|
|
685
|
+
params=model.make_params()
|
|
686
|
+
if p0:
|
|
687
|
+
params['mu'].set(value=float(p0[0]))
|
|
688
|
+
params['V_th'].set(value=float(p0[1]))
|
|
689
|
+
params['R_s'].set(value=float(p0[2]))
|
|
690
|
+
else:
|
|
691
|
+
params['mu'].set(L**2*(ydata.max()-ydata.min())/(C*(xdata.max()-xdata.min())))
|
|
692
|
+
params['V_th'].set(xdata.min())
|
|
693
|
+
params['R_s'].set(1/ydata.max())
|
|
694
|
+
|
|
695
|
+
result=model.fit(ydata,params,x=xdata)
|
|
696
|
+
return result
|
|
697
|
+
|
|
698
|
+
def dynes_fit(xdata,ydata,p0=None,inputinfo=None):
|
|
699
|
+
"""
|
|
700
|
+
Fits x,y data with a Dynes model for a superconducting gap: 'G_N * abs((e*x - i*gamma*e)/(sqrt((e*x - i*gamma*e)**2 - (delta*e)**2)))'
|
|
701
|
+
|
|
702
|
+
Args:
|
|
703
|
+
xdata: x data to fit
|
|
704
|
+
ydata: y data to fit
|
|
705
|
+
p0 (opt.): initial guesses for the parameters. Should be a list of floats.
|
|
706
|
+
Format should be: [G_N, gamma, delta]
|
|
707
|
+
inputinfo: not used.
|
|
708
|
+
Returns:
|
|
709
|
+
result: lmfit result object.
|
|
710
|
+
"""
|
|
711
|
+
electron=1.60217663e-19
|
|
712
|
+
def dynes_model(x,x0, G_N, gamma, delta):
|
|
713
|
+
return np.abs(G_N*((electron*(x-x0)-1j*gamma*electron)/np.sqrt((electron*(x-x0)-1j*gamma*electron)**2-(delta*electron)**2)).real)
|
|
714
|
+
model=Model(dynes_model)
|
|
715
|
+
params=model.make_params()
|
|
716
|
+
if p0:
|
|
717
|
+
params['x0'].set(value=float(p0[0]))
|
|
718
|
+
params['G_N'].set(value=float(p0[1]))
|
|
719
|
+
params['gamma'].set(value=float(p0[2]))
|
|
720
|
+
params['delta'].set(value=float(p0[3]))
|
|
721
|
+
else:
|
|
722
|
+
params['x0'].set(value=(np.max(xdata)+np.min(xdata))/2)
|
|
723
|
+
params['G_N'].set(value=(np.max(ydata)-np.min(ydata))/2)
|
|
724
|
+
params['gamma'].set(value=(np.max(xdata)-np.min(xdata))/200)
|
|
725
|
+
params['delta'].set(value=(np.max(xdata)-np.min(xdata))/2)
|
|
726
|
+
result=model.fit(ydata,params,x=xdata)
|
|
727
|
+
return result
|
|
728
|
+
|
|
729
|
+
def ramsey_fit(xdata,ydata,p0,inputinfo):
|
|
730
|
+
'''
|
|
731
|
+
Fits x,y data with a Ramsey model for T2 of a qubit: 'A*cos(2*pi*f*x + phi)*exp(-x/T2) + B + C*x'
|
|
732
|
+
|
|
733
|
+
Args:
|
|
734
|
+
xdata: x data to fit
|
|
735
|
+
ydata: y data to fit
|
|
736
|
+
p0 (opt.): initial guesses for the parameters. Should be a list of floats.
|
|
737
|
+
Format should be: [A, B, C, f, phi, T2]
|
|
738
|
+
inputinfo: not used.
|
|
739
|
+
Returns:
|
|
740
|
+
result: lmfit result object.
|
|
741
|
+
'''
|
|
742
|
+
def ramsey_model(x, A, B, C, f, phi, T2):
|
|
743
|
+
return A*np.cos(2*np.pi*f*x + phi)*np.exp(-x/T2)+B+C*x
|
|
744
|
+
|
|
745
|
+
model=Model(ramsey_model)
|
|
746
|
+
params=model.make_params()
|
|
747
|
+
if p0:
|
|
748
|
+
params['A'].set(value=float(p0[0]))
|
|
749
|
+
params['B'].set(value=float(p0[1]))
|
|
750
|
+
params['C'].set(value=float(p0[2]))
|
|
751
|
+
params['f'].set(value=float(p0[3]))
|
|
752
|
+
params['phi'].set(value=float(p0[4]))
|
|
753
|
+
params['T2'].set(value=float(p0[5]))
|
|
754
|
+
else:
|
|
755
|
+
params['A'].set(value=(ydata.max()-ydata.min())/2)
|
|
756
|
+
params['B'].set(value=ydata.min())
|
|
757
|
+
params['C'].set(value=(ydata.max()-ydata.min())/100)
|
|
758
|
+
params['f'].set(value=1/(xdata.max()-xdata.min()))
|
|
759
|
+
params['phi'].set(value=0)
|
|
760
|
+
params['T2'].set(value=(xdata.max()-xdata.min())/10)
|
|
761
|
+
|
|
762
|
+
result=model.fit(ydata,params,x=xdata)
|
|
763
|
+
return result
|
|
764
|
+
|
|
765
|
+
def RCSJfit(xdata,ydata,p0=None,inputinfo=None):
|
|
766
|
+
'''
|
|
767
|
+
Fits the differential conductance, dI/dV of a Josephson junction vs the dc voltage, Vdc applied across it.
|
|
768
|
+
|
|
769
|
+
The model is fitted to the derivative of:
|
|
770
|
+
|
|
771
|
+
(Rj/(Rj+Rc))*(jc*Im(I_(1-in(v)(B))/I_(-in(V)(B))) + (Vdc-Vdc_0)/Rj)
|
|
772
|
+
|
|
773
|
+
where:
|
|
774
|
+
- Rj is the junction resistance,
|
|
775
|
+
- Rc is the shunt resistance,
|
|
776
|
+
- jc is the critical current density,
|
|
777
|
+
- Vdc_0 is the offset voltage,
|
|
778
|
+
- I_(1-in(v)(B)) and I_(-in(V)(B)) are modified Bessel functions of the first kind.
|
|
779
|
+
- n(V) = hbar*(Vdc-Vdc_0)/(2*e*Rc*k_B*T), where hbar is the reduced Planck's constant, e is the electron charge, k_B is the Boltzmann constant, and T is the temperature
|
|
780
|
+
- B = jc*hbar/(2*e*k_B*T)
|
|
781
|
+
|
|
782
|
+
See e.g. https://www.science.org/doi/suppl/10.1126/sciadv.aav1235/suppl_file/aav1235_sm.pdf, page 14 onwards.
|
|
783
|
+
|
|
784
|
+
Args:
|
|
785
|
+
xdata: x data to fit (Vdc)
|
|
786
|
+
ydata: y data to fit (dI/dV)
|
|
787
|
+
p0 (opt.): Initial guesses for jc, Rj, Rc, Vdc_0 and c (the constant offset).
|
|
788
|
+
Format should be: [jc, Rj, Rc, Vdc_0, c]
|
|
789
|
+
inputinfo: A list containing the temperature in Kelvin. If not provided, defaults to 0.02 K.
|
|
790
|
+
Format should be: [T]
|
|
791
|
+
'''
|
|
792
|
+
try:
|
|
793
|
+
from mpmath import besseli
|
|
794
|
+
except ImportError:
|
|
795
|
+
print("The mpmath library is required for RCSJ fitting. Please install it using 'pip install mpmath'.")
|
|
796
|
+
|
|
797
|
+
T=inputinfo[0] if inputinfo else 0.02 # Temperature in Kelvin
|
|
798
|
+
def besselfuncdiff(x, jc, Rj, Rc, x_0):
|
|
799
|
+
hbar=1.0545718e-34
|
|
800
|
+
electron=1.60217662e-19
|
|
801
|
+
boltzmann=1.38064852e-23
|
|
802
|
+
bess=np.zeros(np.size(x))
|
|
803
|
+
|
|
804
|
+
for i in range(np.size(x)):
|
|
805
|
+
eta=hbar*(x[i]-x_0)/(2*electron*Rc*boltzmann*T)
|
|
806
|
+
beta=jc*hbar/(2*electron*boltzmann*T)
|
|
807
|
+
bess1 = besseli(1-eta*1j,beta)
|
|
808
|
+
bess2 = besseli(-eta*1j,beta)
|
|
809
|
+
imagine = (bess1/bess2).imag
|
|
810
|
+
bess[i]=(Rj/(Rj+Rc))*(jc*imagine+((x[i]-x_0)/Rj))
|
|
811
|
+
return np.gradient(bess,x)
|
|
812
|
+
|
|
813
|
+
if p0:
|
|
814
|
+
jc_init= float(p0[0])
|
|
815
|
+
Rj_init= float(p0[1])
|
|
816
|
+
Rc_init= float(p0[2])
|
|
817
|
+
x_0_init= float(p0[3])
|
|
818
|
+
c_init=float(p0[4])
|
|
819
|
+
else:
|
|
820
|
+
x_0_init=np.mean(xdata)
|
|
821
|
+
jc_init=1e-9 #honestly have no idea how to provide good initial guesses for this model based only on the data. I think the model is too complex to ever do so.
|
|
822
|
+
Rj_init=1e10
|
|
823
|
+
Rc_init=3600
|
|
824
|
+
c_init=np.min(ydata)
|
|
825
|
+
|
|
826
|
+
model = Model(besselfuncdiff)+lmm.ConstantModel()
|
|
827
|
+
params = model.make_params(jc=jc_init,Rj=Rj_init, Rc=Rc_init, x_0=x_0_init,c=c_init)#, bkg_c=conductance[-1])
|
|
828
|
+
|
|
829
|
+
result = model.fit(ydata, params, x=xdata)
|
|
830
|
+
return result
|
|
831
|
+
|
|
832
|
+
def statistics(xdata,ydata,p0,inputinfo):
|
|
833
|
+
# Not really fitting; just returns the statistical information specified in inputinfo.
|
|
834
|
+
if 'percentile' in inputinfo:
|
|
835
|
+
percentiles=[float(p0[i]) for i in range(len(p0))] if p0 else [1,5,10,25,50,75,90,95,99]
|
|
836
|
+
elif 'average' in inputinfo:
|
|
837
|
+
weights=[float(p0[i]) for i in range(len(p0))] if p0 else None
|
|
838
|
+
elif inputinfo == 'all':
|
|
839
|
+
percentiles=[1,5,10,25,50,75,90,95,99]
|
|
840
|
+
weights=None
|
|
841
|
+
|
|
842
|
+
function_dict={
|
|
843
|
+
'mean':np.mean,
|
|
844
|
+
'average':lambda x: np.average(x,weights=weights),
|
|
845
|
+
'std':np.std,
|
|
846
|
+
'var':np.var,
|
|
847
|
+
'median':np.median,
|
|
848
|
+
'min':np.min,
|
|
849
|
+
'max':np.max,
|
|
850
|
+
'range':lambda x: np.max(x)-np.min(x),
|
|
851
|
+
'sum':np.sum,
|
|
852
|
+
'skew':lambda x: np.mean((x-np.mean(x))**3)/np.std(x)**3,
|
|
853
|
+
'percentile':lambda x: np.percentile(x,percentiles),
|
|
854
|
+
'autocorrelation': lambda x: np.correlate(x,x,mode='full')[len(x)-1:],
|
|
855
|
+
'autocorrelation_norm': lambda x: np.correlate(x,x,mode='full')[len(x)-1:]/np.max(np.correlate(x,x,mode='full'))
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
result={}
|
|
859
|
+
if inputinfo == 'all':
|
|
860
|
+
functions=function_dict.keys()
|
|
861
|
+
elif inputinfo == 'all1d':
|
|
862
|
+
functions=['mean','std','var','median','min','max','range','sum','skew']
|
|
863
|
+
else:
|
|
864
|
+
functions=inputinfo.split(',')
|
|
865
|
+
for function in functions:
|
|
866
|
+
if function not in ['percentile','autocorrelation','autocorrelation_norm']:
|
|
867
|
+
result[function]=float(function_dict[function](ydata))
|
|
868
|
+
else:
|
|
869
|
+
if function == 'percentile':
|
|
870
|
+
result[function]=function_dict[function](ydata)
|
|
871
|
+
result['percentiles']=percentiles
|
|
872
|
+
else:
|
|
873
|
+
result[function]=function_dict[function](ydata)
|
|
874
|
+
result['xdata']=xdata
|
|
875
|
+
result['ydata']=ydata
|
|
876
|
+
return result
|
|
877
|
+
|
|
878
|
+
|
|
879
|
+
# Dictionary for storing info about the fit functions used in this module.
|
|
880
|
+
functions = {}
|
|
881
|
+
functions['Polynomials and powers'] = {'Linear': {'function': linear},
|
|
882
|
+
'Polynomial': {'inputs':'order',
|
|
883
|
+
'default_inputs' : '2',
|
|
884
|
+
'function': polynomial},
|
|
885
|
+
'Power law': {'inputs':'# of terms, use offset',
|
|
886
|
+
'default_inputs': '1,0',
|
|
887
|
+
'parameters': 'a_0,b_0,...,a_n,b_n,c',
|
|
888
|
+
'function': fit_powerlaw},
|
|
889
|
+
'Exponentials': {'inputs':'# of terms, use offset',
|
|
890
|
+
'default_inputs': '1,0',
|
|
891
|
+
'parameters': 'a_0,b_0,...,a_n,b_n,c',
|
|
892
|
+
'function': fit_exponentials}
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
# Give descriptions to help the user.
|
|
896
|
+
functions['Polynomials and powers']['Linear']['description'] = 'Simple linear y = mx + b fit'
|
|
897
|
+
functions['Polynomials and powers']['Polynomial']['description'] = ('Polynomial fit of order n, y = a_n*x^n + ... + a_1*x + a_0.\n'
|
|
898
|
+
'Provide n, the polynomial order.')
|
|
899
|
+
functions['Polynomials and powers']['Power law']['description'] = ('Power law fit of the form y = a_0*x^b_0 + ... + a_n*x^b_n + c.\n'
|
|
900
|
+
'Provide inputs n,c, where n is the number of terms to include '
|
|
901
|
+
'and c is whether to include a constant offset; c=0: no offset, c=1: offset')
|
|
902
|
+
functions['Polynomials and powers']['Exponentials']['description'] = ('Exponential fit of the form y = a_0*exp(-x/b_0) + ... + a_n*exp(-x/b_n) + c.\n'
|
|
903
|
+
'Provide inputs n,c, where n is the number of terms to include '
|
|
904
|
+
'and c is whether to include a constant offset; c=0: no offset, c=1: offset')
|
|
905
|
+
|
|
906
|
+
# Add simple peaks that take amplitude, width and position as only arguments.
|
|
907
|
+
functions['Peaks: 3 param']={}
|
|
908
|
+
multipeak_description= ('Fit one or more {} peaks. The inputs are n,c, where n is the number of peaks and c is whether to '
|
|
909
|
+
'include a constant offset in the fit. c=0 --> no offset, c=1 --> Offset.\n'
|
|
910
|
+
'For exmaple, inputs of 4,0 will fit four peaks without a constant offset.\n'
|
|
911
|
+
'By default, the fit assumes equally spaced peaks with heights approximately the max value of the data.\n'
|
|
912
|
+
'To change this, provide an initial guess of the form {}\n'
|
|
913
|
+
'If providing an intial guess, you must provide '
|
|
914
|
+
'all parameters for all peaks.')
|
|
915
|
+
lorgaussform=('w1 ... wn, a1 ... an, x1 ... xn, c where w = peak sigma a = peak amplitude, '
|
|
916
|
+
'x = peak position and c = constant offset value (if used). For example:\n'
|
|
917
|
+
'0.01 0.014 0.005, 1.1 1.05 1.2, -0.1 0 0.1\n'
|
|
918
|
+
'for three peaks with no constant offset, and\n'
|
|
919
|
+
'0.01 0.014 0.005, 1.1 1.05 1.2, -0.1 0 0.1,5\n'
|
|
920
|
+
'for three peaks with a constant offset of 5.\n')
|
|
921
|
+
|
|
922
|
+
for peaktype in ['Lorentzian','Gaussian','Lognormal','StudentsT','DampedOscillator']:
|
|
923
|
+
functions['Peaks: 3 param'][peaktype] = {}
|
|
924
|
+
functions['Peaks: 3 param'][peaktype]['inputs']='# of peaks, use offset'
|
|
925
|
+
functions['Peaks: 3 param'][peaktype]['default_inputs']='1,0'
|
|
926
|
+
functions['Peaks: 3 param'][peaktype]['parameters']='sigma(s), amplitudes(s), position(s), y_offset'
|
|
927
|
+
functions['Peaks: 3 param'][peaktype]['description'] = multipeak_description.format(peaktype,lorgaussform)
|
|
928
|
+
functions['Peaks: 3 param']['Lorentzian']['function'] = partial(fit_lorgausstype, lmm.LorentzianModel)
|
|
929
|
+
functions['Peaks: 3 param']['Gaussian']['function'] = partial(fit_lorgausstype,lmm.GaussianModel)
|
|
930
|
+
functions['Peaks: 3 param']['StudentsT']['function'] = partial(fit_lorgausstype,lmm.StudentsTModel)
|
|
931
|
+
functions['Peaks: 3 param']['Lognormal']['function'] = partial(fit_lorgausstype,lmm.LognormalModel)
|
|
932
|
+
functions['Peaks: 3 param']['DampedOscillator']['function'] = partial(fit_lorgausstype,lmm.DampedOscillatorModel)
|
|
933
|
+
|
|
934
|
+
|
|
935
|
+
functions['Peaks: 4 param']={}
|
|
936
|
+
fourparamform=('w1 ... wn, a1 ... an, x1 ... xn, g1 ... gn, c where w = peak sigma, a = peak amplitude, '
|
|
937
|
+
'x = peak position, g = gamma (see lmfit documentation for meaning in each case) '
|
|
938
|
+
'and c = constant offset value (if used). For example:\n'
|
|
939
|
+
'0.01 0.014 0.005, 1.1 1.05 1.2, -0.1 0 0.1, 0.001 0.001 0.001\n'
|
|
940
|
+
'for three peaks with no constant offset, and\n'
|
|
941
|
+
'0.01 0.014 0.005, 1.1 1.05 1.2, -0.1 0 0.1, 0.001 0.001 0.001,5\n'
|
|
942
|
+
'for three peaks with a constant offset of 5.\n')
|
|
943
|
+
|
|
944
|
+
for peaktype in ['Voigt','PseudoVoigt','BreitWigner/Fano','SplitLorentzian','ExpGaussian','SkewedGaussian','Moffat','Pearson7','DampedHarmOsc','Doniach']:
|
|
945
|
+
functions['Peaks: 4 param'][peaktype] = {}
|
|
946
|
+
functions['Peaks: 4 param'][peaktype]['inputs']='# of peaks, use offset'
|
|
947
|
+
functions['Peaks: 4 param'][peaktype]['default_inputs']='1,0'
|
|
948
|
+
functions['Peaks: 4 param'][peaktype]['parameters']='sigmas(s), amplitudes(s), position(s), gammas(s), y_offset'
|
|
949
|
+
functions['Peaks: 4 param'][peaktype]['description'] = multipeak_description.format(peaktype,fourparamform)
|
|
950
|
+
functions['Peaks: 4 param']['Voigt']['function'] = partial(fit_voigttype, lmm.VoigtModel)
|
|
951
|
+
functions['Peaks: 4 param']['PseudoVoigt']['function'] = partial(fit_voigttype, lmm.PseudoVoigtModel)
|
|
952
|
+
functions['Peaks: 4 param']['BreitWigner/Fano']['function'] = partial(fit_voigttype, lmm.BreitWignerModel)
|
|
953
|
+
functions['Peaks: 4 param']['SplitLorentzian']['function'] = partial(fit_voigttype, lmm.SplitLorentzianModel)
|
|
954
|
+
functions['Peaks: 4 param']['ExpGaussian']['function'] = partial(fit_voigttype, lmm.ExponentialGaussianModel)
|
|
955
|
+
functions['Peaks: 4 param']['SkewedGaussian']['function'] = partial(fit_voigttype, lmm.SkewedGaussianModel)
|
|
956
|
+
functions['Peaks: 4 param']['Moffat']['function'] = partial(fit_voigttype, lmm.MoffatModel)
|
|
957
|
+
functions['Peaks: 4 param']['Pearson7']['function'] = partial(fit_voigttype, lmm.Pearson7Model)
|
|
958
|
+
functions['Peaks: 4 param']['DampedHarmOsc']['function'] = partial(fit_voigttype, lmm.DampedHarmonicOscillatorModel)
|
|
959
|
+
functions['Peaks: 4 param']['Doniach']['function'] = partial(fit_voigttype, lmm.DoniachModel)
|
|
960
|
+
|
|
961
|
+
functions['Peaks: skewed']={}
|
|
962
|
+
fiveparamform=('w1 ... wn, a1 ... an, x1 ... xn, g1 ... gn, s1 ... sn, c where w = peak sigma, a = peak amplitude, '
|
|
963
|
+
'x = peak position, g = gamma (see lmfit documentation for meaning in each case), '
|
|
964
|
+
's = skew and c = constant offset value (if used). For example:\n'
|
|
965
|
+
'0.01 0.014 0.005, 1.1 1.05 1.2, -0.1 0 0.1, 0.001 0.001 0.001, 0.1 0.12 0.14\n'
|
|
966
|
+
'for three peaks with no constant offset, and\n'
|
|
967
|
+
'0.01 0.014 0.005, 1.1 1.05 1.2, -0.1 0 0.1, 0.001 0.001 0.001, 0.1 0.12 0.14, 5\n'
|
|
968
|
+
'for three peaks with a constant offset of 5.\n')
|
|
969
|
+
for peaktype in ['Pearson4','SkewedVoigt']:
|
|
970
|
+
functions['Peaks: skewed'][peaktype] = {}
|
|
971
|
+
functions['Peaks: skewed'][peaktype]['inputs']='# of peaks, use offset'
|
|
972
|
+
functions['Peaks: skewed'][peaktype]['default_inputs']='1,0'
|
|
973
|
+
functions['Peaks: skewed'][peaktype]['parameters']='sigma(s), amplitude(s), position(s), gammas(s), skews(s), y_offset'
|
|
974
|
+
functions['Peaks: skewed'][peaktype]['description'] = multipeak_description.format(peaktype,fiveparamform)
|
|
975
|
+
functions['Peaks: skewed']['Pearson4']['function'] = partial(fit_skewedpeaks, lmm.Pearson4Model)
|
|
976
|
+
functions['Peaks: skewed']['SkewedVoigt']['function'] = partial(fit_skewedpeaks,lmm.SkewedVoigtModel)
|
|
977
|
+
|
|
978
|
+
# Oscillating
|
|
979
|
+
functions['Oscillating']={'Sine':{'function':fit_sines}
|
|
980
|
+
#'Ramsey':{'function':ramsey_fit},
|
|
981
|
+
# 'Sine w/exp decay':{},
|
|
982
|
+
# 'Sine w/power decay':{}
|
|
983
|
+
}
|
|
984
|
+
functions['Oscillating']['Sine']['inputs']='# of waves, use offset'
|
|
985
|
+
functions['Oscillating']['Sine']['default_inputs']='1,0'
|
|
986
|
+
functions['Oscillating']['Sine']['parameters']='Amplitude(s), frequency(ies), phase(s), offset'
|
|
987
|
+
functions['Oscillating']['Sine']['description']=('Fit a sine wave A*sin(f*x+phase) with optional + c if use offset == 1.\n'
|
|
988
|
+
'Specify # of waves > 1 to fit A_1*sin(f_1*x+ph_1) + ... + A_n*sin(f_n*x+ph_n) + c.\n'
|
|
989
|
+
'Initial guesses for more than one wave take the form\n'
|
|
990
|
+
'A_1 ... A_n, f_1 ... f_n, ph_1 ... ph_n, c\n'
|
|
991
|
+
'e.g., for three waves:\n'
|
|
992
|
+
'0.01 0.014 0.005, 1.1 1.05 1.2, -0.1 0 0.1, 0.001 0.001 0.001,5\n')
|
|
993
|
+
|
|
994
|
+
# Thermal distributions
|
|
995
|
+
functions['Thermal']={'Maxwell':{'fullname':'Maxwell-Boltzmann', 'equation':'1/A*exp((x-x0)/kT)','function':partial(thermal_fit, 'maxwell')},
|
|
996
|
+
'Fermi':{'fullname':'Fermi-Dirac', 'equation':'1/[A*exp((x-x0)/kT)+1]','function':partial(thermal_fit, 'fermi')},
|
|
997
|
+
'Bose':{'fullname':'Bose-Einstein', 'equation':'1/[A*exp((x-x0)/kT)-1]','function':partial(thermal_fit, 'bose')},
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
thermaldescription=('Fit to a {} distribution: y = {}. kT is considered a single fit parameter. Initial guesses '
|
|
1001
|
+
'for kT should be in units of x, usually either eV or J.')
|
|
1002
|
+
for key in functions['Thermal'].keys():
|
|
1003
|
+
functions['Thermal'][key]['parameters']='A, x0, kT'
|
|
1004
|
+
functions['Thermal'][key]['description']=thermaldescription.format(functions['Thermal'][key]['fullname'], functions['Thermal'][key]['equation'])
|
|
1005
|
+
|
|
1006
|
+
|
|
1007
|
+
|
|
1008
|
+
#Step functions
|
|
1009
|
+
functions['Step']={'Linear':{},'Arctan':{},'ErrorFunction':{},'Logistic':{}}
|
|
1010
|
+
stepdescription=('Fit a single step function of type {} (see lmfit documentation for information). \n'
|
|
1011
|
+
'The step function starts at 0 and ends with value +/- A. '
|
|
1012
|
+
'The x-value where y=A/2 is given by x0, and sigma is the characteristic width of the step.\n'
|
|
1013
|
+
'Use an offset filter on the data in the main panel to ensure your data starts at y=0.\n'
|
|
1014
|
+
'In addition, the x-data must be ascending; use a filter to multiply by -1, and possibly an add/subtract offset, if necessary.\n')
|
|
1015
|
+
for key in functions['Step'].keys():
|
|
1016
|
+
functions['Step'][key]['parameters']='A, x0, sigma'
|
|
1017
|
+
functions['Step'][key]['description']=stepdescription.format(key)
|
|
1018
|
+
functions['Step']['Linear']['function'] = partial(step_fit, 'linear')
|
|
1019
|
+
functions['Step']['Arctan']['function'] = partial(step_fit, 'arctan')
|
|
1020
|
+
functions['Step']['ErrorFunction']['function'] = partial(step_fit, 'erf')
|
|
1021
|
+
functions['Step']['Logistic']['function'] = partial(step_fit, 'logistic')
|
|
1022
|
+
|
|
1023
|
+
#Rectangle functions
|
|
1024
|
+
functions['Rectangle']={'Linear':{},'Arctan':{},'ErrorFunction':{},'Logistic':{}}
|
|
1025
|
+
rectdescription=('Fit a rectangle function of type {} (see lmfit documentation for information). \n'
|
|
1026
|
+
'A rectangle function steps from 0 to +/- A, then back to 0. '
|
|
1027
|
+
'The x-values where y=A/2 are given by x0_1, x0_2, and sigma_1 and sigma_2 are the characteristic widths of the steps.\n'
|
|
1028
|
+
'Use an offset filter on the data in the main panel to ensure your data starts at y=0.\n'
|
|
1029
|
+
'In addition, the x-data must be ascending; use a filter to multiply by -1, and possibly an add/subtract offset, if necessary.\n')
|
|
1030
|
+
for key in functions['Rectangle'].keys():
|
|
1031
|
+
functions['Rectangle'][key]['parameters']=['A, x0_1, x0_2, sigma_1, sigma_2']
|
|
1032
|
+
functions['Rectangle'][key]['description']=rectdescription.format(key)
|
|
1033
|
+
|
|
1034
|
+
functions['Rectangle']['Linear']['function'] = partial(rectangle_fit, 'linear')
|
|
1035
|
+
functions['Rectangle']['Arctan']['function'] = partial(rectangle_fit, 'arctan')
|
|
1036
|
+
functions['Rectangle']['ErrorFunction']['function'] = partial(rectangle_fit, 'erf')
|
|
1037
|
+
functions['Rectangle']['Logistic']['function'] = partial(rectangle_fit, 'logistic')
|
|
1038
|
+
|
|
1039
|
+
# Custom fits based on expression fit
|
|
1040
|
+
functions['Custom']={'Coulomb blockade':{'function':QD_fit},
|
|
1041
|
+
'FET mobility':{'function':FET_mobility},
|
|
1042
|
+
'BCS/Dynes':{'function':dynes_fit},
|
|
1043
|
+
'Ramsey':{'function':ramsey_fit},
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
functions['Custom']['Coulomb blockade']['inputs']='# of peaks, alpha'
|
|
1047
|
+
functions['Custom']['Coulomb blockade']['default_inputs']='1,0.1'
|
|
1048
|
+
functions['Custom']['Coulomb blockade']['parameters']='V_0s, G_0s, T'
|
|
1049
|
+
functions['Custom']['Coulomb blockade']['description']=('Fit one or more Coulomb blockade peaks in the limit of low tunnel coupling:\n'
|
|
1050
|
+
'G = G_0 * cosh(e*alpha*(Vg - V_0)/(2*k_B*T))**(-2)\n'
|
|
1051
|
+
'where G is the conductance, G_0 the peak conductance, Vg is the gate voltage, '
|
|
1052
|
+
'V_0 the gate voltage at the peak, k_B is the Boltzmann constant, '
|
|
1053
|
+
'T is the temperature, e is electron charge and alpha is the lever arm.\n'
|
|
1054
|
+
'Alpha must be provided as an input, e.g. 0.02, and should be measured from '
|
|
1055
|
+
'Coulomb diamonds.\n'
|
|
1056
|
+
'Initial guesses for V_0s, G_s0s and T are given in the form:\n'
|
|
1057
|
+
'V_0_1 V_0_2 ... V_0_n, G_0_1 G_0_2 ... G_0_n, T\n'
|
|
1058
|
+
'e.g., -0.1 0 0.1, 1.1 1.05 1.2, 0.01\n'
|
|
1059
|
+
'note: give only a single value for T, as it _should_ be the same for all peaks.\n'
|
|
1060
|
+
'However, each peak will return its own T.\n'
|
|
1061
|
+
'See e.g. page 396 of the Thomas Ihn book "Semiconductor Nanostructures: Quantum States and Electronic Transport" '
|
|
1062
|
+
)
|
|
1063
|
+
|
|
1064
|
+
functions['Custom']['FET mobility']['inputs']='C,L'
|
|
1065
|
+
functions['Custom']['FET mobility']['default_inputs']='5e-15,5e-6'
|
|
1066
|
+
functions['Custom']['FET mobility']['parameters']='mu, V_th, R_s'
|
|
1067
|
+
functions['Custom']['FET mobility']['description']=('Fit a FET mobility curve of the form:\n'
|
|
1068
|
+
'G = 1/(R_s+L^2/(C*mu*(Vg-V_th)))\n'
|
|
1069
|
+
'where R_s is the series resistance, L is the channel length, C is the capacitance, '
|
|
1070
|
+
'mu is the mobility and V_th is the threshold voltage.\n'
|
|
1071
|
+
'The data should be G, conductance, vs Vg, gate voltage.\n'
|
|
1072
|
+
'You must provide calculated/measured values for C and L; these are not fitted.\n'
|
|
1073
|
+
'If necessary, provide initial guesses for mu, x_0 and R_s in the form:\n'
|
|
1074
|
+
'0.1,0.5,1e3\n'
|
|
1075
|
+
'Note; all units in SI! i.e. S and m, not e2/h and cm.\n'
|
|
1076
|
+
'See doi.org/10.1088/0957-4484/26/21/215202')
|
|
1077
|
+
|
|
1078
|
+
functions['Custom']['BCS/Dynes']['parameters']='V0, G_N, gamma, delta'
|
|
1079
|
+
functions['Custom']['BCS/Dynes']['description']=('Fit the BCS/Dynes model to a tunnel spectrum of a superconducting gap. '
|
|
1080
|
+
'The Dynes model is given by:\n'
|
|
1081
|
+
'dI/dV(V) = G_N*Re[(e*(V-V0)-i*gamma)/sqrt((e*(V-V0)-i*gamma)^2-(delta)^2)]\n'
|
|
1082
|
+
'where G_N is the normal state conductance, gamma is broadening and '
|
|
1083
|
+
'delta is the superconducting gap. V0 accounts for any offset due to e.g. miscalibrated current preamplifier\n'
|
|
1084
|
+
'Delta and gamma are in electronvolts\n'
|
|
1085
|
+
'see doi.org/10.1103/PhysRevLett.41.1509\n or doi.org/10.1038/s41467-021-25100-w')
|
|
1086
|
+
|
|
1087
|
+
functions['Custom']['Ramsey']['parameters']='A, B, C, f, phi, T2'
|
|
1088
|
+
functions['Custom']['Ramsey']['description']=('Fit a Ramsey oscillation of the form A*cos(2*pi*f*x + phi)*exp(-x/T2) + B + C*x.\n'
|
|
1089
|
+
'Initial guesses for the parameters are given in the form:\n'
|
|
1090
|
+
'A, B, C, f, phi, T2\n'
|
|
1091
|
+
'e.g., 0.01, -50, -4e6, 40e6, 0, 50e-9\n')
|
|
1092
|
+
|
|
1093
|
+
functions['Custom']['RCSJ fit']={'function':RCSJfit}
|
|
1094
|
+
functions['Custom']['RCSJ fit']['inputs'] = 'T'
|
|
1095
|
+
functions['Custom']['RCSJ fit']['default_inputs'] = '0.02'
|
|
1096
|
+
functions['Custom']['RCSJ fit']['parameters'] = 'jc, Rj, Rc, Vdc_0, c'
|
|
1097
|
+
functions['Custom']['RCSJ fit']['description'] = ('Fit the differential conductance, dI/dV, of a Josephson junction vs the dc voltage, Vdc applied across it.\n'
|
|
1098
|
+
'The model is fitted to the derivative of:\n'
|
|
1099
|
+
'(Rj/(Rj+Rc))*(jc*Im(I_(1-in(v)(B))/I_(-in(V)(B))) + (Vdc-Vdc_0)/Rj)\n'
|
|
1100
|
+
'where:\n'
|
|
1101
|
+
'- Rj is the junction resistance,\n'
|
|
1102
|
+
'- Rc is the shunt resistance,\n'
|
|
1103
|
+
'- jc is the critical current density,\n'
|
|
1104
|
+
'- Vdc_0 is the offset voltage,\n'
|
|
1105
|
+
'- I_(1-in(v)(B)) and I_(-in(V)(B)) are modified Bessel functions of the first kind.\n'
|
|
1106
|
+
'- n(V) = hbar*(Vdc-Vdc_0)/(2*e*Rc*k_B*T), where hbar is the reduced Planck\'s constant, e is the electron charge, k_B is the Boltzmann constant, and T is the temperature\n'
|
|
1107
|
+
'- B = jc*hbar/(2*e*k_B*T)\n'
|
|
1108
|
+
'- Additionally, there is a constant offset, c used in the fit.\n'
|
|
1109
|
+
'See e.g. https://www.science.org/doi/suppl/10.1126/sciadv.aav1235/suppl_file/aav1235_sm.pdf, page 14 onwards.\n'
|
|
1110
|
+
'Input the temperature in Kelvin, T, and the initial guesses for jc, Rj, Rc, Vdc_0 and c.\n'
|
|
1111
|
+
'It is strongly recommended to give your own initial guesses for this fit, there is no general way to define good guesses based solely on the data. Give guesses in the form:\n'
|
|
1112
|
+
'jc, Rj, Rc, Vdc_0, c\n'
|
|
1113
|
+
'e.g., 1e-9, 12000, 3600, 0, 1e-4\n'
|
|
1114
|
+
'Note: the mpmath library is required for this fit, to handle the Bessel functions. Install it with '
|
|
1115
|
+
'pip install mpmath".\n')
|
|
1116
|
+
|
|
1117
|
+
|
|
1118
|
+
#Wrap the ExpressionModel to allow arbitrary input.
|
|
1119
|
+
functions['User input']={'Expression':{}}
|
|
1120
|
+
functions['User input']['Expression']['inputs'] = 'Expression'
|
|
1121
|
+
functions['User input']['Expression']['parameters'] = 'all fit parameters'
|
|
1122
|
+
functions['User input']['Expression']['description'] =('Fit an arbitrary expression. Input any function you like. '
|
|
1123
|
+
'x must be the independent variable. e.g.\n'
|
|
1124
|
+
'C + A * exp(-x/x0) * sin(x*freq+phase)\n'
|
|
1125
|
+
'You must also provide initial guesses for each parameter in the form:\n'
|
|
1126
|
+
'C=0.25, A=1.0, x0=2.0, freq=0.04, phase=0')
|
|
1127
|
+
functions['User input']['Expression']['function']=expression_fit
|
|
1128
|
+
|
|
1129
|
+
functions['Statistics']={'Statistics':{}}
|
|
1130
|
+
functions['Statistics']['Statistics']['inputs'] = 'Function names'
|
|
1131
|
+
functions['Statistics']['Statistics']['parameters'] = 'weights or percentiles (opt)'
|
|
1132
|
+
functions['Statistics']['Statistics']['description'] =('Calculate statistical information about the data. Available numpy functions are:\n'
|
|
1133
|
+
'mean, average, std, var, median, min, max, range, sum, skew, percentile, autocorrelation and autocorrelation_norm.\n'
|
|
1134
|
+
'See: https://numpy.org/doc/stable/reference/routines.statistics.html.\n'
|
|
1135
|
+
'Input a comma-separated list of functions to calculate, e.g.\n'
|
|
1136
|
+
'mean,std,var,median,min,max.\n'
|
|
1137
|
+
'The average can be weighted by providing a list of weights in the "Initial guess" box.\n'
|
|
1138
|
+
'The default percentiles to calculate are 1,5,10,25,50,75,90,95 and 99. '
|
|
1139
|
+
'To change this, provide a list of percentiles in the "Initial guess" box.\n'
|
|
1140
|
+
'User-defined weights and percentiles cannot be used together; calculate the average and percentiles separately in this case.\n'
|
|
1141
|
+
'Use "all" to calculate all functions, or use the keyword "all1d" to calculate everything except the percentiles and autocorrelation.\n'
|
|
1142
|
+
'Linecuts only: A parameter dependency can be generated, but only if the functions return the same number of values.\n'
|
|
1143
|
+
'This means you can generate a dependency that includes everything except the percentiles and autocorrelation; '
|
|
1144
|
+
'these can be generated separately, resulting in a 2D dataset.\n'
|
|
1145
|
+
'The difference between autocorrelation and autocorrelation_norm is that the latter is normalised to the zero-th index value.\n')
|
|
1146
|
+
functions['Statistics']['Statistics']['function']=statistics
|
|
1147
|
+
functions['Statistics']['Statistics']['default_inputs'] = 'all'
|
|
1148
|
+
|
|
1149
|
+
|
|
1150
|
+
# functions to return different parts of the functions dictionary. Makes it easier to call in main
|
|
1151
|
+
def get_class_names():
|
|
1152
|
+
return functions.keys()
|
|
1153
|
+
|
|
1154
|
+
def get_function(function_class,function_name):
|
|
1155
|
+
return functions[function_class][function_name]['function']
|
|
1156
|
+
|
|
1157
|
+
def get_names(fitclass='Polynomials and powers'):
|
|
1158
|
+
return functions[fitclass].keys()
|
|
1159
|
+
|
|
1160
|
+
def get_parameters(function_class,function_name):
|
|
1161
|
+
return functions[function_class][function_name]['parameters']
|
|
1162
|
+
|
|
1163
|
+
def get_description(function_class,function_name):
|
|
1164
|
+
return functions[function_class][function_name]['description']
|
|
1165
|
+
|
|
1166
|
+
# Entry point for fitting the data. Passes info along to relevant functions and returns the fit result
|
|
1167
|
+
def fit_data(function_class,function_name, xdata, ydata, p0=None, inputinfo=None):
|
|
1168
|
+
# all fit functions get called through the dictionary
|
|
1169
|
+
f = functions[function_class][function_name]['function']
|
|
1170
|
+
result = f(xdata,ydata,p0,inputinfo)
|
|
1171
|
+
return result
|