Example 2: Analysis of a resonator fabricated at Argonne National Laboratory¶
By: Faustin Carter, 2016, updated 2018
This notebook imports the data from the Agilent files, creates resonator objects from each datafile, packs those objects into a list, runs a semi-intelligent fitting algorithm predicated on the fact that things don’t change too much from one temperature to the next, and then plots the data and fit curves in a nice way.
Once you’ve understood this example, you should be able to replicate it
with your own data simply be writing a custom process_file function
and updating the code that finds the datafiles.
Note: Before starting this example, you’ll want to read up on list
comprehensions and Python’s built-in map method if you aren’t
already familiar with them.
#Set up the notebook for inline plotting
%matplotlib inline
#For high-res figures. You can comment this out if you don't have a retina screen
%config InlineBackend.figure_format = 'retina'
#Because you should always import numpy!
import numpy as np
Load up the scraps modules¶
You’ll need to change the path to reflect wherever you stored the code
import os
#Add the scraps folder to the path. You can skip this if you used pip to install scraps.
import sys
sys.path.append(os.getcwd()+'scraps/')
#Load up the resonator code!
import scraps as scr
Make a list of Resonator objects¶
Lists are a great way to organize things. scraps includes a tool for
easily indexing lists of Resonator objects by temperature and power.
Each object has a value called itemp that is just the experiment
temperature rounded to the nearest 5 mK. This helps a lot with
organizing data taken at the ‘same’ temperature that has fluctuations.
#Path to data (you should look and see what it looks like)
dataPath = './ExampleData/'
#Define a resonator name. The data folder has data for RES-1, RES-2, RES-3, RES-4 so any of those will work.
#Try changing the name to one of the others and rerun the notebook.
resName = 'RES-1'
#We pass the process file and the path to the data, and the built-in routine spits out a list of Resonator objects!
resList = scr.makeResList(scr.process_file, dataPath, resName, skiprows=1)
#Create index vectors for all temps and pwrs in the experiment
#itemp = temp rounded to the nearest 5 mK
tempsList = np.unique([res.temp for res in resList])
pwrsList = np.unique([res.pwr for res in resList])
#Let's look at all the temperatures and powers in the experiment
print('number of resonators in list = ', len(resList))
print('temps = ', tempsList)
print('powers = ', pwrsList)
print('temps x powers = ', len(tempsList)*len(pwrsList))
#Can you figure out why the number of resonators in resList is different from temps*powers?
#Hint: look in the data folder at the names of the data files and compare temperatures.
number of resonators in list = 255
temps = [ 0.089 0.095 0.098 0.1 0.111 0.112 0.113 0.125 0.126 0.127
0.141 0.142 0.143 0.158 0.159 0.16 0.161 0.175 0.176 0.177
0.193 0.195 0.196 0.213 0.214 0.215 0.234 0.235 0.25 0.251
0.271 0.272 0.31 0.341 0.384 0.423 0.463 0.511 0.548 0.587
0.628 0.629 0.668 0.669 0.707 0.749 0.75 0.787 0.788 0.829
0.83 0.87 0.871 0.913 0.914 0.955 0.956 0.997 0.998 1.035
1.036 1.037 1.069 1.071 1.072 1.105 1.107 1.145 1.146 1.147
1.187 1.189 1.191 1.225 1.226 1.227 1.228 1.229 1.339 1.34
1.343 1.344 1.415 1.419 1.42 1.421 1.422 1.494 1.495 1.568
1.569 1.571 1.573 1.643 1.644 1.647 1.649 1.65 1.727 1.73
1.733 1.809 1.811 1.813 1.814 1.817 1.888 1.889 1.892 1.894
1.973 1.974 2.037 2.04 2.044 2.045 2.047 2.112 2.115 2.119
2.121 2.181 2.189 2.19 2.195 2.196 2.263 2.265 2.266 2.268
2.343 2.347 2.348 2.353 2.357 2.402 2.409 2.41 2.416 2.417
2.468 2.477 2.478 2.482 2.487]
powers = [-50. -40. -30. -20. -10.]
temps x powers = 725
Fit I and Q data to a model¶
This fits I and Q data simultaneously using the built in cmplxIQ
routine.
for res in resList:
#Load in the params
res.load_params(scr.cmplxIQ_params)
#Do the fit
res.do_lmfit(scr.cmplxIQ_fit)
Make a pretty plot¶
Fits aren’t worth anything if you don’t plot the results!!
figA = scr.plotResListData(resList,
plot_types=['IQ', #Real vs Imaginary part of S21
'rIQ', #residual of fits in IQ plane
'LogMag', #Magnitude of S21 vs frequency
'Phase'], #Phase of S21 vs frequency
color_by='temps',
fig_size=4,
powers=[-20],
num_cols = 2,
force_square = True,
plot_fits = [False]*4) #<-- need to specify a boolean for each plot type
Use the ResonatorSweep object to look at fit parameters¶
Usually what we are really interested in is the fit parameters as functions of temperature at different powers. There is a nice object we can use to look at this easily.
#Pack all of the fit data into resSweep, and use the 'block' index method
#to figure out which temperatures are nominally the same
resSweep = scr.ResonatorSweep(resList, index='block')
#Look at the first few frequencies:
print('\nf0\n',resSweep['f0'].head())
#and the first few reduced-chi-squared values
print('\nredchi\n',resSweep['redchi'].head())
f0
-50.0 -40.0 -30.0 -20.0 -10.0
96.0 8.174975e+09 8.174912e+09 8.174903e+09 8.174897e+09 8.174890e+09
112.0 8.174932e+09 8.174883e+09 8.174874e+09 8.174870e+09 8.174866e+09
126.0 8.174914e+09 8.174854e+09 8.174842e+09 8.174839e+09 8.174835e+09
142.0 8.174889e+09 8.174819e+09 8.174810e+09 8.174806e+09 8.174806e+09
160.0 8.174859e+09 8.174799e+09 8.174785e+09 8.174787e+09 8.174789e+09
redchi
-50.0 -40.0 -30.0 -20.0 -10.0
96.0 1.495088 22.843695 28.455501 38.214780 258.204772
112.0 6.112850 28.353847 32.134005 75.482957 532.721916
126.0 1.889137 20.585032 58.884990 42.484026 146.677615
142.0 2.398259 19.541072 35.529321 48.696257 35.188619
160.0 1.862983 59.830557 35.014426 53.491934 38.018223
#Now let's make a plot of some of the parameters!
figS = scr.plotResSweepParamsVsX(resSweep,
plot_keys=['gain0', 'f0', 'qi', 'qc', 'df', 'redchi'],
num_cols = 3,
xvals='temperature')
#Or maybe you just want to look at how Q varies with power at different temperatures:
figS2 = scr.plotResSweepParamsVsX(resSweep,
plot_keys=['qc', 'qi'],
fig_size = 5,
xvals='power')
#Now you should try running the emcee fitter on all the traces and compare the plots.
#You can get the mc output by asking to plot 'f0_mc' instead of 'f0', for instance.