2D Scans¶
2D scans can be created by inheriting from the Scan2D
class. 2D scans are similar to 1D scans with the following
changes:
Two scan models are registered
Two sets of scan points are defined and returned by
get_scan_points()
(one for each dimension).The
calculate_dim0(self, dim1_model)
callback must be implemented in the scan.The
point
andi_point
parameters passed to callback methods will be lists with two entries containing the point and point index of each dimension.
To create a 2D scan, inherit from the Scan2D
class, register two models, and return a list from
get_scan_points()
with two entries containing the scan points for both dimensions. The dimension
argument
passed to register_model()
specifies the dimension of the model. Dimension 1 is the sub-scan, and dimension 0
is the top level scan.
The calculate_dim0
Callback¶
The calculate_dim0(self, dim1_model)
callback returns a fitted parameter or a calculated value along with the error
in that value after a sub-scan completes. After a sub-scan completes and a fit has been performed on that sub-scan ,
calculate_dim0()
will be called and passed the dimension 1 model. The dimension 1 model can then be used in
calculate_dim0()
to fetch the fitted parameters or other data needed to calculate the value and error to
return. The value returned will be plotted as the y-value in the dimension 0 plot. The error returned will weight that
value when the final fit is performed along dimension 0 using the y-values and errors returned by calculate_dim0()
.
The corresponding x-values used by the dimension 0 fit are the scan points of the top level (dimension 0) scan.
Example 2D Scan¶
An example of a 2D scan that scans over the RF trap frequency and performs a sub-scan over tickle frequencies might look like:
from scan_framework.scans import *
from scan_framework.models import *
from lib.cooling import *
from lib.detection import *
class RfResonatorScan(Scan2D, EnvExperiment):
def build(self):
super().build()
# devices
self.setattr_device('ttl_tickle')
self.setattr_device('dds_tickle')
# libs
self.cooling = Cooling(self)
self.detection = Detection(self)
# arguments
self.setattr_argument('rf_frequencies', Scannable(
default=RangeScan(
start=64.44 * MHz,
stop=64.48 * MHz,
npoints=20
),
unit='MHz',
scale=1 * MHz,
ndecimals=4
), group='Scan Range')
self.setattr_argument('tickle_frequencies', Scannable(
default=RangeScan(
start=4.6 * MHz,
stop=4.8 * MHz,
npoints=50
),
unit='MHz',
scale=1 * MHz,
ndecimals=4
), group='Scan Range')
# scan arguments
self.scan_arguments()
def prepare(self):
# Dimension 0 model (top level, RF trap frequencies)
self.rf_model = ScanModel(self,
namespace="rf_resonator",
fit_function = fitting.Lor,
main_fit = 'x0')
self.register_model(self.rf_model,
dimension=0,
# Peform a final fit on the fitted parameters from each sub-scan
fit=True,
set=True)
# Dimension 1 model (sub-scan level, tickle frequencies)
self.tickle_model = ScanModel(self,
fit_function = fit_functions.SincInv
main_fit = 'frequency')
self.register_model(self.tickle_model,
dimension=1,
# Data is collected by the dimension 1 model only
measurement=True,
# Do save the fitted param values to the current_scan namespace
set=True,
# Don't save the fitted frequency as the current tickle freq
save=False)
def get_scan_points(self):
# assign the trap frequencies and tickle frequencies as the scan points
return [
self.rf_frequencies, # dimension 0 (trap freqs)
self.tickle_frequencies, # dimension 1 (tickle freqs)
]
@kernel
def set_scan_point(self, i_point, point):
trap_freq = point[0]
tickle_freq = point[1]
# set the trap frequency at the start of the tickle sub-scan
self.core.break_realtime()
if i_point[1] == 0:
self.dds_rf.set(trap_freq)
delay(3*us)
# set the tickle frequency
self.dds_tickle.set(tickle_freq)
delay(3*us)
@kernel
def measure(self, point):
# cool
self.cooling.doppler()
# pulse the tickle TTL
self.ttl_tickle.pulse(100*us)
# detect
counts = self.detection.detect()
return counts
def calculate_dim0(self, dim1_model):
# plot this dimension 1 fitted value in the dimension 0 plot
param = dim1_model.fit.params.frequency
# weight final fit by error in this dimension 1 fit param
error = dim1_model.fit.errs.frequency_err
return param, error