This page was generated from docs/examples/15_minutes_to_QCoDeS.ipynb. Interactive online version: Binder badge.

15 minutes to QCoDeS

This short introduction is aimed mainly for beginners. Before you start with your first code using QCoDeS, make sure you have properly set up the Python environment for QCoDeS as explained in this document.


An experimental setup comprises of many instruments. We call an experimental setup as “station”. A station is connected to many instruments or devices. QCoDeS provides a way to interact with all these instruments to help users the measurements and store the data in a database. To interact (read, write, trigger, etc) with the instruments, we have created a library of drivers for commonly used ones. These drivers implement the most needed functionalities of the instruments.

An “Instrument” can perform many functions. For example, on an oscilloscope instrument, we first set a correct trigger level and other parameters and then obtain a trace. In QCoDeS lingo, we call “trigger_level” and “trace” as parameter of this instrument. An instrument at any moment will have many such parameters which together define the state of the instrument, hence a parameter can be thought of as a state variable of the instrument. QCoDeS provides a method to set values of these parameters (set trigger level) and get the values from them (obtain a trace). By this way, we can interact with all the needed parameters of an instrument and are ready to set up a measurement.

QCoDeS has a similar programmatic structure, as well. QCoDeS structure comprises of a Station class which is a bucket of objects from Instrument class containing many objects from Parameter class. The value of these parameters are set and measured during a measurement. The Measurement class provides a context manager for registering the parameters and providing a link between different parameters. The measured data is stored in a database.

Here, we will briefly discuss how you can set up your own experiment with the help of QCoDeS.



If you are using QCoDeS as your main data acquisition framework, a typical Python script at your disposal may look like:

%matplotlib inline
import os
from time import sleep

import matplotlib.pyplot as plt
import numpy as np
import qcodes as qc
from qcodes import (
from qcodes.dataset.plotting import plot_dataset
from qcodes.logger.logger import start_all_logging
from qcodes.tests.instrument_mocks import DummyInstrument, DummyInstrumentWithMeasurement

We strongly recommend not to import unused packages to increase readability of your code.


In every measurement session, it is highly recommended to have QCoDeS logging turned on. This will allow you to have all the logs in case troubleshooting is required. To enable logging, we can either add the following single line of code at the beginnig of our scripts after the imports:

Logging hadn't been started.
Activating auto-logging. Current session state plus future input saved.
Filename       : /home/runner/.qcodes/logs/command_history.log
Mode           : append
Output logging : True
Raw input log  : False
Timestamping   : True
State          : active
Qcodes Logfile : /home/runner/.qcodes/logs/210924-2964-qcodes.log

or we can configure qcodes to automatically start logging on every import of qcodes, by running the following code once. (This will persist the current configuration in ~\qcodesrc.json)

from qcodes import config
config.logger.start_logging_on_import = 'always'

You can find the log files at “.qcodes” directory, typically located at your home folder (e.g., see the corresponding path to the “Filename” key above). This path contains two log files: - command_history.log: contains the commands executed.

And in this particular case - 191113-13960-qcodes.log: contains python logging information. The file is named as [date (YYMMDD)]-[process id]-[qcodes].log. The display message from start_all_logging() function shows that the Qcodes Logfile is saved at C:\Users\a-halakh\.qcodes\logs\191113-13960-qcodes.log

Station creation

A station is a collection of all the instruments and devices present in your experiment. As mentioned earlier, it can be thought of as a bucket where you can add your instruments, parameters and other components. Each of these terms has a definite meaning in QCoDeS and shall be explained in later sections. Once a station is properly configured, you can use its instances to access these components. We refer to tutorial on Station for more details.

We start with instantiating a station class which at the moment does not comprise of any instruments or parameters.

station = qc.Station()


We can look at all the instruments and the parameters inside this station bucket using snapshot method. Since at the moment we have not added anything to our station, the snapshot will contain the names of the keys with no values:

{'instruments': {}, 'parameters': {}, 'components': {}, 'config': None}

The snapshot of the station is categorized as the dictionary of all the instruments,parameters, components and list of default_measurement. Once you have populated your station you may want to look at the snapshot again.


Instrument class in Qcodes is responsible for holding connections to hardware, creating a parameter or method for each piece of functionality of the instrument. For more information on instrument class we refer to the detailed description here or the corresponding api documentation.

Let us, now, create two dummy instruments and associate two parameters for each of them:

# A dummy instrument dac with two parameters ch1 and ch2
dac = DummyInstrument('dac', gates=['ch1', 'ch2'])

# A dummy instrument that generates some real looking output depending
# on the values set on the setter_instr, in this case the dac
dmm = DummyInstrumentWithMeasurement('dmm', setter_instr=dac)

Aside from the bare snapshot, which returns a Python dictionary, a more readable form can be returned via:

        parameter value
IDN :   None
ch1 :   0 (V)
ch2 :   0 (V)
        parameter value
IDN :   None
v1  :   0 (V)
v2  :   0 (V)

Add instruments into station

Every instrument that you are working with during an experiment should be added to the instance of the Station class. Here, we add the dac and dmm instruments by using add_component method:

Add components


Remove component

We use the method remove_component to remove a component from the station. For example you can remove dac as follows:

<DummyInstrument: dac>
{'dmm': <DummyInstrumentWithMeasurement: dmm>}

Let us add the dac instrument back:


Station snapshot

As there are two instruments added to the station object, the snapshot will include all the properties associated with them:

{'instruments': {'dmm': {'functions': {},
   'submodules': {},
   '__class__': 'qcodes.tests.instrument_mocks.DummyInstrumentWithMeasurement',
   'parameters': {'IDN': {'__class__': 'qcodes.instrument.parameter.Parameter',
     'full_name': 'dmm_IDN',
     'value': {'vendor': None,
      'model': 'dmm',
      'serial': None,
      'firmware': None},
     'raw_value': {'vendor': None,
      'model': 'dmm',
      'serial': None,
      'firmware': None},
     'ts': '2021-09-24 13:26:58',
     'label': 'IDN',
     'instrument': 'qcodes.tests.instrument_mocks.DummyInstrumentWithMeasurement',
     'instrument_name': 'dmm',
     'inter_delay': 0,
     'unit': '',
     'name': 'IDN',
     'vals': '<Anything>',
     'post_delay': 0},
    'v1': {'__class__': 'qcodes.tests.instrument_mocks.DmmExponentialParameter',
     'full_name': 'dmm_v1',
     'value': 4.871105744975052,
     'raw_value': 4.871105744975052,
     'ts': '2021-09-24 13:26:58',
     'label': 'Gate v1',
     'instrument': 'qcodes.tests.instrument_mocks.DummyInstrumentWithMeasurement',
     'instrument_name': 'dmm',
     'inter_delay': 0,
     'unit': 'V',
     'name': 'v1',
     'vals': '<Numbers -800<=v<=400>',
     'post_delay': 0},
    'v2': {'__class__': 'qcodes.tests.instrument_mocks.DmmGaussParameter',
     'full_name': 'dmm_v2',
     'value': 0.760266283210377,
     'raw_value': 0.760266283210377,
     'ts': '2021-09-24 13:26:58',
     'label': 'Gate v2',
     'instrument': 'qcodes.tests.instrument_mocks.DummyInstrumentWithMeasurement',
     'instrument_name': 'dmm',
     'inter_delay': 0,
     'unit': 'V',
     'name': 'v2',
     'vals': '<Numbers -800<=v<=400>',
     'post_delay': 0}},
   'name': 'dmm'},
  'dac': {'functions': {},
   'submodules': {},
   '__class__': 'qcodes.tests.instrument_mocks.DummyInstrument',
   'parameters': {'IDN': {'__class__': 'qcodes.instrument.parameter.Parameter',
     'full_name': 'dac_IDN',
     'value': {'vendor': None,
      'model': 'dac',
      'serial': None,
      'firmware': None},
     'raw_value': {'vendor': None,
      'model': 'dac',
      'serial': None,
      'firmware': None},
     'ts': '2021-09-24 13:26:58',
     'label': 'IDN',
     'instrument': 'qcodes.tests.instrument_mocks.DummyInstrument',
     'instrument_name': 'dac',
     'inter_delay': 0,
     'unit': '',
     'name': 'IDN',
     'vals': '<Anything>',
     'post_delay': 0},
    'ch1': {'__class__': 'qcodes.instrument.parameter.Parameter',
     'full_name': 'dac_ch1',
     'value': 0,
     'raw_value': 0,
     'ts': '2021-09-24 13:26:58',
     'label': 'Gate ch1',
     'instrument': 'qcodes.tests.instrument_mocks.DummyInstrument',
     'instrument_name': 'dac',
     'inter_delay': 0,
     'unit': 'V',
     'name': 'ch1',
     'vals': '<Numbers -800<=v<=400>',
     'post_delay': 0},
    'ch2': {'__class__': 'qcodes.instrument.parameter.Parameter',
     'full_name': 'dac_ch2',
     'value': 0,
     'raw_value': 0,
     'ts': '2021-09-24 13:26:58',
     'label': 'Gate ch2',
     'instrument': 'qcodes.tests.instrument_mocks.DummyInstrument',
     'instrument_name': 'dac',
     'inter_delay': 0,
     'unit': 'V',
     'name': 'ch2',
     'vals': '<Numbers -800<=v<=400>',
     'post_delay': 0}},
   'name': 'dac'}},
 'parameters': {},
 'components': {},
 'config': None}

Station Configurator

The instantiation of the instruments, that is, setting up the proper initial values of the corresponding parameters and similar pre-specifications of a measurement constitutes the initialization portion of the code. In general, this portion can be quite long and tedious to maintain. These (and more) concerns can be solved by a YAML configuration file of the Station object. We refer to the notebook on station for more details.


A QCoDeS Parameter has the property that it is settable, gettable or both. Let us clarify this with an example of a real instrument, say an oscilloscope. An oscilloscope contains settings such as trigger mode, trigger level, source etc. Most of these settings can be set to a particular value in the instrument. For example, trigger mode can be set to ‘edge’ mode and trigger level to some floating number. Hence, these parameters are called settable. Similarly, the parameters that we are able to retrieve the values currently associated with them are called gettable. In this example notebook, we have a ‘dac’ instrument with ‘ch1’ and ‘ch2’ are added as its Parameters. Similarly, we have a ‘dmm’ instrument with ‘v1’ and ‘v2’ are added as its Parameters. We also note that, apart from the trivial use of Parameter as the standard parameter of the instrument, it can be used as a common variable to utilize storing/retrieving data. Furthermore, it can be used as a subclass in more complex design cases.

QCoDeS provides following parameter classes built in:

  • Parameter : Represents a single value at a given time. Example: voltage.

  • ParameterWithSetpoints: Represents an array of values of all the same type that are returned all at once. Example: voltage vs time waveform . We refer to the notebook in which more detailed examples concerning the use cases of this parameter can be found.

  • DelegateParameter: It is intended for proxy-ing other parameters. You can use different label, unit, etc in the delegated parameter as compared to the source parameter.

  • MultiParameter: Represents a collection of values with different meanings and possibly different dimensions. Example: I and Q, or I vs time and Q vs time.

Most of the times you can use these classes directly and use the get, set functions to get or set the values to those parameters. But sometimes it may be useful to subclass the above classes, in that case you should define get_raw and set_raw methods rather then get or set methods. The get_raw, set_raw method is automatically wrapped to provide a get, set method on the parameter instance. Overwriting get in subclass of above parameters or the _BaseParameter is not allowed and will throw a runtime error.

To understand more about parameters consult the notebook on Parameter for more details.

In most cases, a settable parameter accepts its value as a function argument. Let us set the a value of 1.1 for the ‘ch1’ parameter of the ‘dac’ instrument:


Similarly, we ask the current value of a gettable parameter with a simple function call. For example, the output voltage of dmm can be read via


Further information can be found in the user guide or api documentation of parameter.

Initialise database and experiment

Before starting a measurement, we first initialise a database. The location of the database is specified by the configuration object of the QCoDeS installation. The database is created with the latest supported version complying with the QCoDeS version that is currently under use. If a database already exists but an upgrade has been done to the QCoDeS, then that database can continue to be used and it is going to be upgraded to the latest version automatically at first connection.

The initialisation of the database is achieved via:

Upgrading database; v0 -> v1: : 0it [00:00, ?it/s]
Upgrading database; v1 -> v2: 100%|██████████| 1/1 [00:00<00:00, 772.15it/s]
Upgrading database; v2 -> v3: : 0it [00:00, ?it/s]
Upgrading database; v3 -> v4: : 0it [00:00, ?it/s]
Upgrading database; v4 -> v5: 100%|██████████| 1/1 [00:00<00:00, 920.01it/s]
Upgrading database; v5 -> v6: : 0it [00:00, ?it/s]
Upgrading database; v6 -> v7: 100%|██████████| 1/1 [00:00<00:00, 443.61it/s]
Upgrading database; v7 -> v8: 100%|██████████| 1/1 [00:00<00:00, 983.19it/s]
Upgrading database; v8 -> v9: 100%|██████████| 1/1 [00:00<00:00, 1158.33it/s]

As the result, a database according to the current QCoDeS configuration is created, which as per the default configuration, a database called “experiments.db” is created in the user’s home folder. Let’s check the database location and name:


Alternatively, if you already have a QCoDeS database which you would like to use for your measurement, it is sufficient to use


Note that it is user’s responsibility to provide the correct path for the existing database. The notation of the path may differ with respect to the operating system. The method initialise_or_create_database_at makes sure that your QCoDeS session is connected to the referred database. If the database file does not exist, it will be created at the provided path:

Upgrading database; v0 -> v1: : 0it [00:00, ?it/s]
Upgrading database; v1 -> v2: 100%|██████████| 1/1 [00:00<00:00, 1097.70it/s]
Upgrading database; v2 -> v3: : 0it [00:00, ?it/s]
Upgrading database; v3 -> v4: : 0it [00:00, ?it/s]
Upgrading database; v4 -> v5: 100%|██████████| 1/1 [00:00<00:00, 966.21it/s]
Upgrading database; v5 -> v6: : 0it [00:00, ?it/s]
Upgrading database; v6 -> v7: 100%|██████████| 1/1 [00:00<00:00, 286.79it/s]
Upgrading database; v7 -> v8: 100%|██████████| 1/1 [00:00<00:00, 729.70it/s]
Upgrading database; v8 -> v9: 100%|██████████| 1/1 [00:00<00:00, 822.90it/s]

If we check the database location again, it should be changed to ./my_data.db, because under the hood, initialise_or_create_database_at connects to the database in the provided path by changing the db_location to that path:


Change location of database

In case you would like to change the location of the database directly, for example, to the current working directory, it is sufficient to assign the new path as the value of the corresponding key db_location:

cwd = os.getcwd()
qc.config["core"]["db_location"] = os.path.join(cwd, 'testing.db')

Note that any change in the qcodes configuration in a Python kernel is a temporary change in that kernel (means it does not permanently change the configuration file unless it is saved in the file). Users should be careful changing the config file (refer to the end of the notebook to learn more about QCoDeS configuration).

Load or create experiment

After initialising the database we create the Experiment object. This object contains the name of the experiment and the sample, and the path of the database. You can use load_or_create_experiment to find and return an experiment with the given experiment and sample name if it already exists, or create one if not found.

exp = load_or_create_experiment(experiment_name='dataset_context_manager',
                                sample_name="no sample1")
Upgrading database; v0 -> v1: : 0it [00:00, ?it/s]
Upgrading database; v1 -> v2: 100%|██████████| 1/1 [00:00<00:00, 519.80it/s]
Upgrading database; v2 -> v3: : 0it [00:00, ?it/s]
Upgrading database; v3 -> v4: : 0it [00:00, ?it/s]
Upgrading database; v4 -> v5: 100%|██████████| 1/1 [00:00<00:00, 402.52it/s]
Upgrading database; v5 -> v6: : 0it [00:00, ?it/s]
Upgrading database; v6 -> v7: 100%|██████████| 1/1 [00:00<00:00, 420.10it/s]
Upgrading database; v7 -> v8: 100%|██████████| 1/1 [00:00<00:00, 741.96it/s]
Upgrading database; v8 -> v9: 100%|██████████| 1/1 [00:00<00:00, 1108.14it/s]

The path of the database for Experiment is the defined path in the QCoDeS configuration. First, Experiment loads the database in that path (or it creates one if there is no database in that path), and then saves the created experiment in that database. Although loading/ creating database by Experiment is a user-friendly feature, we recommend users to initialise their database, as shown earlier, before loading/ creating their experiment, because it allows them to better control their experiments and databases for their measurement.

The method shown above to load or create the experiment is the most versatile one. However for specific cases, the following alternative methods can be used to create or load experiments:

# load_experiment_by_name(experiment_name='dataset_context_manager',sample_name="no sample")
# load_last_experiment()
# load_experiment(1)
# new_experiment(experiment_name='dataset_context_manager',sample_name="no sample")


Qcodes Measurement module provides a context manager for registering parameters to measure and store results. The measurement is first linked to the correct experiment and to the station by passing them as arguments. If no arguments are given, the latest experiment and station are taken as defaults. A keyword argument name can also be set as any string value for Measurement. This set name argument will be used as the name of the resulting dataset.

QCoDeS is capable of storing relations between the parameters, i.e., which parameter is independent and which parameter depends on another one. This capability is later used to make useful plots, where the knowledge of interdependencies is used to define the corresponding variables for the coordinate axes. The required (mandatory) parameters in the measurement are first registered. If there is an interdependency between any given two or more parameters, the independent one is declared as a ‘setpoint’. In our example, dac.ch1 is the independent parameter and dmm.v1 is the dependent parameter whose setpoint is dac.ch1.

meas = Measurement(exp=exp, station=station, name='xyz_measurement')
meas.register_parameter(dac.ch1)  # register the first independent parameter
meas.register_parameter(dmm.v1, setpoints=(dac.ch1,))  # now register the dependent oone

meas.write_period = 2

with as datasaver:
    for set_v in np.linspace(0, 25, 10):
        get_v = dmm.v1.get()
        datasaver.add_result((dac.ch1, set_v),
                             (dmm.v1, get_v))

    dataset = datasaver.dataset  # convenient to have for plotting
Starting experimental run with id: 1.

The returns a context manager for the experiment run. Entering the context returns the DataSaver object to the datasaver variable. The DataSaver class handles the saving of data to the database using the method add_result. The add_result method validates the sizes of all the data points and store them intermittently into a private variable. Within every write-period of the measurement, the data of the private variable is flushed to the database.

meas.write_period is used to define the periods after which the data is committed to the database. We do not commit individual datapoints during measurement to the database but only after some amount of data is collected in stipulated time period (in this case for 2 seconds). The default value of write_period is 5 seconds.

Measurement without defining an Experiment

If we initialise a database but do not create/ load an experiment before running a Measurement, one of the two following outcomes would happen: 1. if the initialised database does not contain any Experiment, then the Measurement will not run and an error related to the Experiment will be thrown; 2. if the database already contains one/ more Experiment, then creating a Measurement object will automatically pick up the latest Experiment from the database, and the meaurement will be performed.

Therefore, creating/ loading an Experiment is a prerequisite for running a Measurement.

Data exploration

List all the experiments in the database

The list of experiments that are stored in the database can be called back as follows:

[dataset_context_manager#no sample1#1@/home/runner/work/Qcodes/Qcodes/docs/examples/testing.db

While our example database contains only few experiments, in reality the database will contain several experiments containing many datasets. Seldom, you would like to load a dataset from a particular experiment for further analysis. Here we shall explore different ways to find and retrieve already measured dataset from the database.

List all the datasets in the database

Let us now retrieve the datasets stored within the current experiment via:

[xyz_measurement #1@/home/runner/work/Qcodes/Qcodes/docs/examples/testing.db
 dac_ch1 - numeric
 dmm_v1 - numeric]

Load the data set using one or more specifications

The method load_by_run_spec can be used to load a run with given specifications such as ‘experiment name’ and ‘sample name’:

dataset = load_by_run_spec(experiment_name='dataset_context_manager', captured_run_id=1)

While the arguments are optional, the function call will raise an error if more than one run matching the supplied specifications is found. If such an error occurs, the traceback will contain the specifications of the runs, as well. Further information concerning ‘Uniquely identifying and loading runs’ can be found in this example notebook.

For more information on the DataSet object that load_by_run_spec returned, refer to DataSet class walkthrough article.

Plot dataset

We arrived at a point where we can visualize our data. To this end, we use the plot_dataset method with dataset as its argument:

([<AxesSubplot:title={'center':'Run #1, Experiment dataset_context_manager (no sample1)'}, xlabel='Gate ch1 (V)', ylabel='Gate v1 (V)'>],

For more detailed examples of plotting QCoDeS datasets, refer to the following articles:

Get data of specific parameter of a dataset

If you are interested in numerical values of a particular parameter within a given dataset, the corresponding data can be retrieved by using get_parameter_data method:

{'dac_ch1': {'dac_ch1': array([ 0.        ,  2.77777778,  5.55555556,  8.33333333, 11.11111111,
         13.88888889, 16.66666667, 19.44444444, 22.22222222, 25.        ])}}
{'dmm_v1': {'dmm_v1': array([5.12910957, 2.88561342, 1.68347197, 0.95778683, 0.51934167,
         0.37716979, 0.19289483, 0.09589269, 0.14957657, 0.03721336]),
  'dac_ch1': array([ 0.        ,  2.77777778,  5.55555556,  8.33333333, 11.11111111,
         13.88888889, 16.66666667, 19.44444444, 22.22222222, 25.        ])}}

We refer reader to exporting data section of the performing measurements using qcodes parameters and dataset and Accessing data in DataSet notebook for further information on get_parameter_data method.

Export data to pandas dataframe

If desired, any data stored within a QCoDeS database can also be exported as pandas dataframes. This can be achieved via:

df = dataset.to_pandas_dataframe_dict()['dmm_v1']
0.000000 5.129110
2.777778 2.885613
5.555556 1.683472
8.333333 0.957787
11.111111 0.519342

Export data to xarray

It’s also possible to export data stored within a QCoDeS database to an xarray.DataArray. This can be achieved via:

xarray = dataset.to_xarray_dataarray_dict()['dmm_v1']
<xarray.DataArray 'dmm_v1' (dac_ch1: 5)>
array([5.12910957, 2.88561342, 1.68347197, 0.95778683, 0.51934167])
  * dac_ch1  (dac_ch1) float64 0.0 2.778 5.556 8.333 11.11
Attributes: (12/22)
    name:                     dmm_v1
    paramtype:                numeric
    label:                    Gate v1
    unit:                     V
    inferred_from:            []
    depends_on:               ['dac_ch1']
    ...                       ...
    captured_counter:         1
    run_id:                   1
    run_description:          {"version": 3, "interdependencies": {"paramspec...
    parent_dataset_links:     []
    run_timestamp_raw:        1632490018.493565
    completed_timestamp_raw:  1632490018.537041

We refer to example notebook on working with pandas and Accessing data in DataSet notebook for further information.

Explore the data using an interactive widget

Experiments widget presents the most important information at a glance, has buttons to plot the dataset and easily explore a snapshot, enabled users to add a note to a dataset.

It is only available in the Jupyter notebook because it uses `ipywidgets <>`__ to display an interactive elements.

Use it in the following ways:

# import it first
from qcodes.interactive_widget import experiments_widget

# and then just run it

# you can pass a specific database path

# you can also pass a specific list of DataSets:
# say, you're only interested in datasets of a particular experiment
experiments = qcodes.experiments()
data_sets = experiments[2].data_sets()

# you can change the sorting of the datasets
# by passing None, "run_id", "timestamp" as sort_by argument:

Here’s a short video that summarizes the looks and the features:

video demo about experiments widget should show here

Things to remember

QCoDeS configuration

QCoDeS uses a JSON based configuration system. It is shipped with a default configuration. The default config file should not be overwritten. If you have any modifications, you should save the updated config file on your home directory or in the current working directory of your script/notebook. The QCoDeS config system first looks in the current directory for a config file and then in the home directory for one and only then - if no config files are found - it falls back to using the default one. The default config is located in qcodes.config. To know how to change and save the config please refer to the documentation on config.

QCoDeS instrument drivers

We support and provide drivers for most of the instruments currently in use at the Microsoft stations. However, if more functionalities than the ones which are currently supported by drivers are required, one may update the driver or request the features form QCoDeS team. You are more than welcome to contribute and if you would like to have a quick overview on how to write instrument drivers, please refer to the example notebooks on writing drivers.

QCoDeS measurements live plotting with Plottr

Plottr supports and is recommended for QCoDeS measurements live plotting. How to use plottr with QCoDeS for live plotting notebook contains more information.