Getting started

In the following we will set up a Jupyter notebook to collect and plot data from a dummy instrument to understand the basic usage of qcodes++. No measurement hardware is required.

Setup

Assuming you installed qcodes++ as described in the installation instructions, you can start Jupyter lab from the Anaconda prompt or command line by activating the environment:

conda activate qcodespp

and starting Jupyter lab:

jupyter lab

This will open a Jupyter lab window in your browser. In the Jupyter lab, create a new Python 3 notebook and run

import qcodespp as qc
from qcodespp.instrument_drivers.dummy import DummyMeasurementInstrument

This code imports the top level of the qcodes++ package, and the DummyMeasurementInstrument driver. In general the instrument drivers are python classes which can be used to create a specific connection with a specific instrument. In this case, we ‘connect’ to our Instrument by running:

instrument = DummyMeasurementInstrument(name='instrument')

As discussed in the background section, qcodes has a virtual Station which contains all the Instrument s and Parameter s. We can initialise the Station and add our `Instrument to it by running:

station = qc.Station(add_variables=globals())

This command creates a Station object and checks all the python variables defined in the notebook. If the variable is a qcodes Instrument or Parameter, it is added to the Station.

We should also tell qcodes++ where to save the data. This is done by running:

qc.set_data_folder('data')

Getting and setting parameters

The dummy instrument has two outputs for providing voltages, and two inputs for measuring voltages from the imaginary device. You can set the output by putting a number in brackets, e.g.

instrument.output1(1.2)

or

instrument.output2(5.9)

and read the value it is set to by leaving the brackets empty, e.g.

instrument.output1()

or

instrument.output2()

To read the input values, again use empty brackets, e.g.

instrument.input1()

or

instrument.input2()

Running a measurement

So far no data has been collected; we’ve just communicated with the instrument. To collect data, we need to create a Loop, which defines the independent parameter(s) that we want to vary. In this case, we will vary the output1 parameter from 0 to 10 volts in steps of 0.1 volt, and measure both the input1 and input2 parameters at each step. For a simple 1D measurement like this, we can use:

loop = qc.loop1d(sweep_parameter=instrument.output1,
                start=0,stop=10,num=101,delay=0.1,
                device_info='dummy', instrument_info='ACdiv=1e5 DCdiv=1e3 freq=123 Hz',
                measure=[instrument.input1, instrument.input2])

Here, we have created the object loop. Inside of it, is a DataSetPP object (accessed via loop.data_set), which will hold the measurements. The details of the DataSetPP are printed. You will see the data will be saved in the ‘data’ folder we specified earlier, and the name of the data includes a counter with a unique number as well as the date and time of the measurement. The rest of the name is generated from the independent parameter settings and the text provided in device_info and instrument_info.

To run the measurement, we can invoke the run() method of the loop object, and tell it which parameters to plot:

data=loop.run([instrument.input1, instrument.input2])

A live plot window will be opened, showing measurements of the two parameters. The run() method returns the DataSetPP object, which can also be reloaded later.

That really is how easy it is to collect data with qcodes++!

Two dimensions

If you need to loop over two independent parameters, you can use the loop2D function. For example, if we want to vary both output1 and output2, we can do:

loop=qc.loop2d(sweep_parameter=instrument.output1,
                start=0,stop=10,num=11,delay=0.1,
                step_parameter=instrument.output2,
                step_start=0,step_stop=10,step_num=11,step_delay=0.1
                device_info='dummy',
                instrument_info='ACdiv=1e5 DCdiv=1e3 freq=123 Hz',
                measure=[instrument.input1, instrument.input2])

This function ‘steps’ instrument.output2, and at every step, it sweeps instrument.output1, and at each point on that sweep, the parameters in measure are measured.

Again, we are given information about the DataSetPP, which shows the array shapes are now indeed two dimensional.

Running the measurement is again just

loop.run([instrument.input1, instrument.input2])

Note that in a loop2d, the sweep_parameter jumps from the stop value back to the start value every time the step_parameter is incremented. This may not be desired behaviour if your sweep_parameter is a sensitive object, e.g. a gate on a nanoelectronic device. In this case, you have two options. Firstly, you can use the loop2dUD function, where for each increment of the step_parameter, the sweep_parameter sweeps from start to stop, then from stop to start again. The code is otherwise identical.

loop=qc.loop2dUD(sweep_parameter=instrument.output1,
                start=0,stop=10,num=11,delay=0.1,
                step_parameter=instrument.output2,
                step_start=0,step_stop=10,step_num=11,step_delay=0.1
                device_info='dummy',
                instrument_info='ACdiv=1e5 DCdiv=1e3 freq=123 Hz',
                measure=[instrument.input1, instrument.input2])

However, you will now see that the dataset contains two lots of data for each parameter, representing the two directions of the sweep parameter’s journey.

The other option you have is to turn on the snake behaviour in loop2d. This alters the direction of the sweep_parameter every alternate step of the step_parameter. This is done by setting the snake attribute to True:

loop=qc.loop2d(sweep_parameter=instrument.output1,
                start=0,stop=10,num=11,delay=0.1,
                step_parameter=instrument.output2,
                step_start=0,step_stop=10,step_num=11,step_delay=0.1
                device_info='dummy',
                instrument_info='ACdiv=1e5 DCdiv=1e3 freq=123 Hz',
                snake=True,
                measure=[instrument.input1, instrument.input2])

Here is a visualisation of the three types of 2D loops:

Types of 2D loops

The loop2dUD function has the option to sweep the sweep_parameter with a fewer number of increments on the return sweep. To set this scaling factor, provide an integer to the fast_down attribute. For example, if you want to sweep the sweep_parameter with 101 increments on the up-sweep, and only 20 increments on the down-sweep, you can do:

loop=qc.loop2dUD(sweep_parameter=instrument.output1,
                start=0,stop=10,num=101,delay=0.1,
                step_parameter=instrument.output2,
                step_start=0,step_stop=10,step_num=11,step_delay=0.1
                device_info='dummy',
                instrument_info='ACdiv=1e5 DCdiv=1e3 freq=123 Hz',
                fast_down=5,
                measure=[instrument.input1, instrument.input2])

Higher dimensions

If you have three or more independent parameters, you will need to manually construct your loop, by explicitly using the Loop class, and/or with the help of python loops.