Getting Started with Scans¶
As an example of how to use the scan framework to create a scan of an experimental parameter, below steps through how to create a 1D scan over a set of frequencies.
Creating a 1D Scan¶
To create a 1D scan, a few basic things are needed:
Include the scan framework classes using
from scan_framework.scans import *
Create an experiment that inherits from
scan_framework.scans.scan.Scan1D
.(optional) Call
super().build()
to create theself.core
andself.scheduler
devices.Call
self.scan_arguments()
in the build method.(optional) Create an instance of a scan model and register it by calling
register_model()
in eitherbuild()
orprepare()
.Implement the
get_scan_points()
method to return the scan points. Typically this just returns a GUI argument of typeScannable
, but can return any object that is iterable such as a list or a numpy array.Implement the
measure()
method.
Inheriting from Scan1D
provides all the logic that is needed for executing a scan. The scan framework
then interfaces with your class by over-ridding the run()
method and calling two template methods that must be
implemented in your class – get_scan_points()
, and measure()
. The get_scan_points()
method returns a list of scan points that the framework will automatically loop over, and the measure()
method
will be called multiple times at each scan point within the main loop (once at each repetition of the scan point).
The measure()
method performs a measurement at the current scan point and returns the value of that measurement
as an integer (typically a number of integer PMT counts). You may also write your own run()
method, which
allows for greater control over the procedural flow of a scan. See the source code for the
run()
method in the Scan
class.
It is easiest to copy and paste that entire run method into your class and make modifications as necessary. A few
GUI arguments for controlling the execution of the scan are also needed. These are created by calling
self.scan_arguments()
in the build()
method.
Finally, if you wish to use the scan framework to process the data that is generated, a scan model needs to be instantiated
and registered with the framework. This is done by calling register_model()
. There are a number of
arguments to the register_model()
method that instruct the framework how to use your model – such as
if it should calculate statistics or perform a fit on the statistics generated (see register_model()
for a full listing of these arguments). If you need to perform custom statistics or simply want more control over, say,
fitting, you can opt to not register a model and perform statistics calculations and fitting manually within the scan.
Typically, this is not needed, however. Please consult Full API documentation for scans for details on how to perform manual
data processing.
Following these requirements a typical, but basic, 1D scan might look something like:
# include all classes needed for writing scan experiments
from scan_framework.scans import *
# include your scan model class
from my_repository.models.my_model import MyModel
class MyScan(Scan1D, EnvExperiment):
def build(self):
# create self.core and self.scheduler
super().build()
# create the scan range GUI argument
self.setattr_argument('frequencies', Scannable(default=RangeScan(
start=450*MHz,
stop=550*MHz,
npoints=50),
scale=1*MHz,
ndecimals=4,
unit="MHz"))
# create GUI arguments for configuring the scan
self.scan_arguments()
def prepare(self):
# create an instance of your scan model
self.model = MyModel(self)
# register your scan model with the framework
# setting measurement=True
# instructs the framework to calculate statistics using the model and to also
# store all collected or calculated data under the model's namespace
#
# setting fit=True
# instructs the framework to perform a fit on the calculated mean values using
# the fit function specified by the model's `fit_function` attribute
self.register_model(self.model, measurement=True, fit=True)
def get_scan_points(self):
# return the set of scan points to the framework
return self.frequencies
@kernel
def measure(self, point):
# `point` is set to the value of the current scan point.
# In this case it is the current frequency in `self.frequencies`
# ... ARTIQ commands to perform measurement: e.g. cooling, pulse sequences, detection.
# return the result of the measurement as an integer (i.e. PMT counts)
return counts
Creating a Scan Model¶
To process the data collected by a scan, a scan model is also needed. All processing of scan data and data handling
is performed by the scan model, which is a Python class that extends from ScanModel
and has built-in data processing capabilities. In its most basic form, a scan model only needs to define a
namespace
attribute, which specifies the dataset key under which
all data will be saved. If the model is to be used for fitting, additionally the
fit_function
and
main_fit
attributes need to be specified.
A very basic scan model might look something like:
# include all classes needed for creating scan models
from scan_framework.models import *
class MyScanModel(ScanModel):
# All datasets will be created under the dataset key given by the namespace attribute.
namespace = 'microwaves.%transition'
# Specifies what fit function to use
@property
def fit_function(self):
if self.type == 'frequency':
return fit_functions.RabiSpectrum
if self.type == 'time':
return fit_functions.Sine
# Specifies the fit param of interest.
# This fit param will be broadcast and persisted to the datasets and is visible in the dashboard
# when the fit passes any defined validations (more on validations later in this guide)
@property
def main_fit(self):
if self.type == 'frequency':
return 'frequency'
if self.type == 'time':
return 'pi_time'
Here, the fit_function
property specifies what fit function to use during fitting and the main_fit
property specifies the name of the fitted param of interest that is to be saved for later use (e.g. a transition
frequency or a pi time). If ‘Fit and Save’ is selected in the GUI, and a fit was successful, the fit param named
by main_fit
will be broadcast, persisted, and saved to the datasets.
Note
By default, all dataset except the main_fit
dataset are created with broadcast=False, persist=False, save=True
as to
not clutter up the datasets in the dashboard but still be available in the hdf5 file for post-processing. See
the Using models for data processing section for more details.
Finally, the %transition
portion of the namespace
attribute is an optional token that will be replaced with
the value of the scan model attribute named transition
, if it exists.