From 75c041a87f3b4600662ab704601b02eef8d5142d Mon Sep 17 00:00:00 2001 From: "William H.P. Nielsen" Date: Tue, 4 Apr 2017 11:22:49 +0200 Subject: [PATCH 01/49] Driver/Keysight DMM (#556) * feat: Add a Keysight_34465A driver Add a first file for a driver for the Keysight 34465A DMM and a benchmarking notebook * Add trigger parameters to driver Add four trigger parameters to the driver. * refactor: Remove 'READ?' as volt get_cmd Make a more general get_cmd for the volt parameter. Using simply 'READ?' is wrong in many cases. * feat: Add sampling parameters Add four parameters for sampling. * fix: Fix wrong volt parameter get_cmd * WIP: Add Array parameter * feat: Add functioning data buffer parameter * Move notebook * Respond to review --- ...gilent 34411A versus Keysight 34465A.ipynb | 442 ++++++++++++++++++ ...gilent 34411A versus Keysight 34465A.ipynb | 442 ++++++++++++++++++ qcodes/__init__.py | 1 + .../Keysight/Keysight_34465A.py | 400 ++++++++++++++++ 4 files changed, 1285 insertions(+) create mode 100644 docs/examples/Agilent 34411A versus Keysight 34465A.ipynb create mode 100644 docs/examples/benchmarking/Agilent 34411A versus Keysight 34465A.ipynb create mode 100644 qcodes/instrument_drivers/Keysight/Keysight_34465A.py diff --git a/docs/examples/Agilent 34411A versus Keysight 34465A.ipynb b/docs/examples/Agilent 34411A versus Keysight 34465A.ipynb new file mode 100644 index 000000000000..0699f323e3db --- /dev/null +++ b/docs/examples/Agilent 34411A versus Keysight 34465A.ipynb @@ -0,0 +1,442 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ + "# Agilent 34411A versus Keysight 34465A\n", + "\n", + "The following notebook perfoms a benchmarking of the two DMMs. In part one, raw readings of immediate voltages are timed\n", + "and compared. In part two, actual sweeps are performed with a QDac." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": true, + "deletable": true, + "editable": true + }, + "outputs": [], + "source": [ + "%matplotlib notebook\n", + "import time\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "import qcodes as qc\n", + "from qcodes.instrument_drivers.Keysight.Keysight_34465A import Keysight_34465A\n", + "from qcodes.instrument_drivers.agilent.Agilent_34400A import Agilent_34400A\n", + "from qcodes.instrument_drivers.QDev.QDac import QDac" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ + "## Import and setup" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Connected to: Keysight Technologies 34465A (serial:MY54505388, firmware:A.02.14-02.40-02.14-00.49-02-01) in 6.93s\n", + "Connected to: Agilent Technologies 34411A (serial:MY48002016, firmware:2.35-2.35-0.09-46-09) in 0.06s\n" + ] + } + ], + "source": [ + "ks = Keysight_34465A('Keysight', 'TCPIP0::K-000000-00000::inst0::INSTR')\n", + "agi = Agilent_34400A('Agilent', 'TCPIP0::192.168.15.105::inst0::INSTR')\n", + "qdac = QDac('qdac', 'ASRL4::INSTR')" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": true, + "deletable": true, + "editable": true + }, + "outputs": [], + "source": [ + "station = qc.Station(ks, agi, qdac)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true + }, + "outputs": [], + "source": [ + "# set up a NPLC of 0.06 corresponding to an aperture time of 1 ms\n", + "ks.NPLC(0.06)\n", + "agi.NPLC(0.06)\n", + "\n", + "# set the same range on both DMMs\n", + "ks.range(1)\n", + "agi.range(1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ + "## Part one - raw readings" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Reading with displays ON\n", + "----------\n", + "Read 512 values one at a time from Keysight in: 8.587134838104248 s\n", + "Read 512 values one at a time from Agilent in: 4.099916696548462 s\n", + "----------\n", + "Read 1024 values one at a time from Keysight in: 16.83395004272461 s\n", + "Read 1024 values one at a time from Agilent in: 8.229825019836426 s\n", + "----------\n", + "Read 2048 values one at a time from Keysight in: 33.76206350326538 s\n", + "Read 2048 values one at a time from Agilent in: 16.453675985336304 s\n", + "----------\n" + ] + } + ], + "source": [ + "# Preliminary testing, just get N readings from each instrument\n", + "# with the displays on\n", + "\n", + "ks.display_clear() # make sure that the display is on\n", + "agi.display_clear()\n", + "\n", + "print('Reading with displays ON')\n", + "print('-'*10)\n", + "\n", + "for N in [512, 1024, 2048]:\n", + "\n", + " t_start = time.time()\n", + " for ii in range(N):\n", + " ks.volt()\n", + " t_stop = time.time()\n", + " print('Read {} values one at a time from Keysight in: {} s'.format(N, t_stop-t_start))\n", + "\n", + " t_start = time.time()\n", + " for ii in range(N):\n", + " agi.volt()\n", + " t_stop = time.time()\n", + " print('Read {} values one at a time from Agilent in: {} s'.format(N, t_stop-t_start))\n", + "\n", + " print('-'*10)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Reading with displays OFF\n", + "----------\n", + "Read 512 values one at a time from Keysight in: 4.5132529735565186 s\n", + "Read 512 values one at a time from Agilent in: 4.098856449127197 s\n", + "----------\n", + "Read 1024 values one at a time from Keysight in: 8.996399164199829 s\n", + "Read 1024 values one at a time from Agilent in: 8.227899074554443 s\n", + "----------\n", + "Read 2048 values one at a time from Keysight in: 17.969755172729492 s\n", + "Read 2048 values one at a time from Agilent in: 16.399696350097656 s\n", + "----------\n" + ] + } + ], + "source": [ + "# The same test, but with the displays off to decrease latency\n", + "\n", + "print('Reading with displays OFF')\n", + "print('-'*10)\n", + "\n", + "for N in [512, 1024, 2048]:\n", + "\n", + " ks.display_text('QCoDeS')\n", + " t_start = time.time()\n", + " for ii in range(N):\n", + " ks.volt()\n", + " t_stop = time.time()\n", + " print('Read {} values one at a time from Keysight in: {} s'.format(N, t_stop-t_start))\n", + " ks.display_clear()\n", + "\n", + " agi.display_text('QCoDes')\n", + " t_start = time.time()\n", + " for ii in range(N):\n", + " agi.volt()\n", + " t_stop = time.time()\n", + " print('Read {} values one at a time from Agilent in: {} s'.format(N, t_stop-t_start))\n", + " agi.display_clear()\n", + "\n", + " print('-'*10)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true + }, + "source": [ + "## Part two - QCoDeS looping" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ + "### 1D Sweep" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DataSet:\n", + " mode = DataMode.LOCAL\n", + " location = 'data/2017-03-07/#018_testsweep_12-32-13'\n", + " | | | \n", + " Setpoint | qdac_ch42_v_set | ch42_v | (512,)\n", + " Measured | Keysight_volt | volt | (512,)\n", + "started at 2017-03-07 12:32:29\n", + "\n", + "\n", + "Did a 512-point QCoDeS sweep of the QDac/Keysight pair in 16.5 s\n", + "\n", + "\n", + "DataSet:\n", + " mode = DataMode.LOCAL\n", + " location = 'data/2017-03-07/#019_testsweep_12-32-29'\n", + " | | | \n", + " Setpoint | qdac_ch41_v_set | ch41_v | (512,)\n", + " Measured | Agilent_volt | volt | (512,)\n", + "started at 2017-03-07 12:32:46\n", + "\n", + "\n", + "Did a 512-point QCoDeS sweep of the QDac/Agilent pair in 16.5 s\n", + "\n", + "\n" + ] + } + ], + "source": [ + "# Sweep a voltage from 0.2 V to 0.5 V in 512 steps\n", + "# Switch off displays for speed\n", + "\n", + "ks.display_text('QCoDeS')\n", + "agi.display_text('QCoDeS')\n", + "\n", + "N = 512 \n", + "V1 = 0.2\n", + "V2 = 0.5\n", + "dV = (V2-V1)/(N-1) # endpoint included in sweep\n", + "\n", + "loop = qc.Loop(qdac.ch42_v.sweep(V1, V2, dV)).each(ks.volt)\n", + "data = loop.get_data_set(name='testsweep')\n", + "t_start = time.time()\n", + "_ = loop.run()\n", + "t_stop = time.time()\n", + "print('\\n\\nDid a {}-point QCoDeS sweep of the QDac/Keysight pair in {:.1f} s\\n\\n'.format(N, t_stop-t_start))\n", + "\n", + "loop = qc.Loop(qdac.ch41_v.sweep(V1, V2, dV)).each(agi.volt)\n", + "data = loop.get_data_set(name='testsweep')\n", + "t_start = time.time()\n", + "_ = loop.run()\n", + "t_stop = time.time()\n", + "print('\\n\\nDid a {}-point QCoDeS sweep of the QDac/Agilent pair in {:.1f} s\\n\\n'.format(N, t_stop-t_start))\n", + "\n", + "agi.display_clear()\n", + "ks.display_clear()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ + "### 2D Sweep" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DataSet:\n", + " mode = DataMode.LOCAL\n", + " location = 'data/2017-03-07/#020_testsweep_12-33-46'\n", + " | | | \n", + " Setpoint | qdac_ch42_v_set | ch42_v | (100,)\n", + " Setpoint | qdac_ch41_v_set | ch41_v | (100, 100)\n", + " Measured | Keysight_volt | volt | (100, 100)\n", + "started at 2017-03-07 12:39:09\n", + "\n", + "\n", + "Did a 100x100-point QCoDeS sweep of the QDac/Keysight pair in 323.9 s\n", + "\n", + "\n", + "DataSet:\n", + " mode = DataMode.LOCAL\n", + " location = 'data/2017-03-07/#021_testsweep_12-39-09'\n", + " | | | \n", + " Setpoint | qdac_ch41_v_set | ch41_v | (100,)\n", + " Setpoint | qdac_ch42_v_set | ch42_v | (100, 100)\n", + " Measured | Agilent_volt | volt | (100, 100)\n", + "started at 2017-03-07 12:44:33\n", + "\n", + "\n", + "Did a 100x100-point QCoDeS sweep of the QDac/Agilent pair in 323.8 s\n", + "\n", + "\n" + ] + } + ], + "source": [ + "# Perform the same sweep as before, but this time nested inside another sweep of the same length,\n", + "# i.e. at each point of the original sweep, another QDac channel sweeps its voltage in the same number of steps\n", + "\n", + "ks.display_text('QCoDeS')\n", + "agi.display_text('QCoDeS')\n", + "\n", + "N = 100\n", + "V1 = 0.2\n", + "V2 = 0.5\n", + "dV = (V2-V1)/(N-1)\n", + "\n", + "loop = qc.Loop(qdac.ch42_v.sweep(V1, V2, dV)).loop(qdac.ch41_v.sweep(V1, V2, dV)).each(ks.volt)\n", + "data = loop.get_data_set(name='testsweep')\n", + "t_start = time.time()\n", + "_ = loop.run()\n", + "t_stop = time.time()\n", + "print('\\n\\nDid a {}x{}-point QCoDeS sweep of the QDac/Keysight pair in {:.1f} s\\n\\n'.format(N, N, t_stop-t_start))\n", + "\n", + "loop = qc.Loop(qdac.ch41_v.sweep(V1, V2, dV)).loop(qdac.ch42_v.sweep(V1, V2, dV)).each(agi.volt)\n", + "data = loop.get_data_set(name='testsweep')\n", + "t_start = time.time()\n", + "_ = loop.run()\n", + "t_stop = time.time()\n", + "print('\\n\\nDid a {}x{}-point QCoDeS sweep of the QDac/Agilent pair in {:.1f} s\\n\\n'.format(N, N, t_stop-t_start))\n", + "\n", + "ks.display_clear()\n", + "agi.display_clear()" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "collapsed": true, + "deletable": true, + "editable": true + }, + "outputs": [], + "source": [ + "ks.close()\n", + "agi.close()\n", + "qdac.close()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true, + "deletable": true, + "editable": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/examples/benchmarking/Agilent 34411A versus Keysight 34465A.ipynb b/docs/examples/benchmarking/Agilent 34411A versus Keysight 34465A.ipynb new file mode 100644 index 000000000000..0699f323e3db --- /dev/null +++ b/docs/examples/benchmarking/Agilent 34411A versus Keysight 34465A.ipynb @@ -0,0 +1,442 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ + "# Agilent 34411A versus Keysight 34465A\n", + "\n", + "The following notebook perfoms a benchmarking of the two DMMs. In part one, raw readings of immediate voltages are timed\n", + "and compared. In part two, actual sweeps are performed with a QDac." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": true, + "deletable": true, + "editable": true + }, + "outputs": [], + "source": [ + "%matplotlib notebook\n", + "import time\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "import qcodes as qc\n", + "from qcodes.instrument_drivers.Keysight.Keysight_34465A import Keysight_34465A\n", + "from qcodes.instrument_drivers.agilent.Agilent_34400A import Agilent_34400A\n", + "from qcodes.instrument_drivers.QDev.QDac import QDac" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ + "## Import and setup" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Connected to: Keysight Technologies 34465A (serial:MY54505388, firmware:A.02.14-02.40-02.14-00.49-02-01) in 6.93s\n", + "Connected to: Agilent Technologies 34411A (serial:MY48002016, firmware:2.35-2.35-0.09-46-09) in 0.06s\n" + ] + } + ], + "source": [ + "ks = Keysight_34465A('Keysight', 'TCPIP0::K-000000-00000::inst0::INSTR')\n", + "agi = Agilent_34400A('Agilent', 'TCPIP0::192.168.15.105::inst0::INSTR')\n", + "qdac = QDac('qdac', 'ASRL4::INSTR')" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": true, + "deletable": true, + "editable": true + }, + "outputs": [], + "source": [ + "station = qc.Station(ks, agi, qdac)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true + }, + "outputs": [], + "source": [ + "# set up a NPLC of 0.06 corresponding to an aperture time of 1 ms\n", + "ks.NPLC(0.06)\n", + "agi.NPLC(0.06)\n", + "\n", + "# set the same range on both DMMs\n", + "ks.range(1)\n", + "agi.range(1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ + "## Part one - raw readings" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Reading with displays ON\n", + "----------\n", + "Read 512 values one at a time from Keysight in: 8.587134838104248 s\n", + "Read 512 values one at a time from Agilent in: 4.099916696548462 s\n", + "----------\n", + "Read 1024 values one at a time from Keysight in: 16.83395004272461 s\n", + "Read 1024 values one at a time from Agilent in: 8.229825019836426 s\n", + "----------\n", + "Read 2048 values one at a time from Keysight in: 33.76206350326538 s\n", + "Read 2048 values one at a time from Agilent in: 16.453675985336304 s\n", + "----------\n" + ] + } + ], + "source": [ + "# Preliminary testing, just get N readings from each instrument\n", + "# with the displays on\n", + "\n", + "ks.display_clear() # make sure that the display is on\n", + "agi.display_clear()\n", + "\n", + "print('Reading with displays ON')\n", + "print('-'*10)\n", + "\n", + "for N in [512, 1024, 2048]:\n", + "\n", + " t_start = time.time()\n", + " for ii in range(N):\n", + " ks.volt()\n", + " t_stop = time.time()\n", + " print('Read {} values one at a time from Keysight in: {} s'.format(N, t_stop-t_start))\n", + "\n", + " t_start = time.time()\n", + " for ii in range(N):\n", + " agi.volt()\n", + " t_stop = time.time()\n", + " print('Read {} values one at a time from Agilent in: {} s'.format(N, t_stop-t_start))\n", + "\n", + " print('-'*10)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Reading with displays OFF\n", + "----------\n", + "Read 512 values one at a time from Keysight in: 4.5132529735565186 s\n", + "Read 512 values one at a time from Agilent in: 4.098856449127197 s\n", + "----------\n", + "Read 1024 values one at a time from Keysight in: 8.996399164199829 s\n", + "Read 1024 values one at a time from Agilent in: 8.227899074554443 s\n", + "----------\n", + "Read 2048 values one at a time from Keysight in: 17.969755172729492 s\n", + "Read 2048 values one at a time from Agilent in: 16.399696350097656 s\n", + "----------\n" + ] + } + ], + "source": [ + "# The same test, but with the displays off to decrease latency\n", + "\n", + "print('Reading with displays OFF')\n", + "print('-'*10)\n", + "\n", + "for N in [512, 1024, 2048]:\n", + "\n", + " ks.display_text('QCoDeS')\n", + " t_start = time.time()\n", + " for ii in range(N):\n", + " ks.volt()\n", + " t_stop = time.time()\n", + " print('Read {} values one at a time from Keysight in: {} s'.format(N, t_stop-t_start))\n", + " ks.display_clear()\n", + "\n", + " agi.display_text('QCoDes')\n", + " t_start = time.time()\n", + " for ii in range(N):\n", + " agi.volt()\n", + " t_stop = time.time()\n", + " print('Read {} values one at a time from Agilent in: {} s'.format(N, t_stop-t_start))\n", + " agi.display_clear()\n", + "\n", + " print('-'*10)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true + }, + "source": [ + "## Part two - QCoDeS looping" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ + "### 1D Sweep" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DataSet:\n", + " mode = DataMode.LOCAL\n", + " location = 'data/2017-03-07/#018_testsweep_12-32-13'\n", + " | | | \n", + " Setpoint | qdac_ch42_v_set | ch42_v | (512,)\n", + " Measured | Keysight_volt | volt | (512,)\n", + "started at 2017-03-07 12:32:29\n", + "\n", + "\n", + "Did a 512-point QCoDeS sweep of the QDac/Keysight pair in 16.5 s\n", + "\n", + "\n", + "DataSet:\n", + " mode = DataMode.LOCAL\n", + " location = 'data/2017-03-07/#019_testsweep_12-32-29'\n", + " | | | \n", + " Setpoint | qdac_ch41_v_set | ch41_v | (512,)\n", + " Measured | Agilent_volt | volt | (512,)\n", + "started at 2017-03-07 12:32:46\n", + "\n", + "\n", + "Did a 512-point QCoDeS sweep of the QDac/Agilent pair in 16.5 s\n", + "\n", + "\n" + ] + } + ], + "source": [ + "# Sweep a voltage from 0.2 V to 0.5 V in 512 steps\n", + "# Switch off displays for speed\n", + "\n", + "ks.display_text('QCoDeS')\n", + "agi.display_text('QCoDeS')\n", + "\n", + "N = 512 \n", + "V1 = 0.2\n", + "V2 = 0.5\n", + "dV = (V2-V1)/(N-1) # endpoint included in sweep\n", + "\n", + "loop = qc.Loop(qdac.ch42_v.sweep(V1, V2, dV)).each(ks.volt)\n", + "data = loop.get_data_set(name='testsweep')\n", + "t_start = time.time()\n", + "_ = loop.run()\n", + "t_stop = time.time()\n", + "print('\\n\\nDid a {}-point QCoDeS sweep of the QDac/Keysight pair in {:.1f} s\\n\\n'.format(N, t_stop-t_start))\n", + "\n", + "loop = qc.Loop(qdac.ch41_v.sweep(V1, V2, dV)).each(agi.volt)\n", + "data = loop.get_data_set(name='testsweep')\n", + "t_start = time.time()\n", + "_ = loop.run()\n", + "t_stop = time.time()\n", + "print('\\n\\nDid a {}-point QCoDeS sweep of the QDac/Agilent pair in {:.1f} s\\n\\n'.format(N, t_stop-t_start))\n", + "\n", + "agi.display_clear()\n", + "ks.display_clear()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ + "### 2D Sweep" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DataSet:\n", + " mode = DataMode.LOCAL\n", + " location = 'data/2017-03-07/#020_testsweep_12-33-46'\n", + " | | | \n", + " Setpoint | qdac_ch42_v_set | ch42_v | (100,)\n", + " Setpoint | qdac_ch41_v_set | ch41_v | (100, 100)\n", + " Measured | Keysight_volt | volt | (100, 100)\n", + "started at 2017-03-07 12:39:09\n", + "\n", + "\n", + "Did a 100x100-point QCoDeS sweep of the QDac/Keysight pair in 323.9 s\n", + "\n", + "\n", + "DataSet:\n", + " mode = DataMode.LOCAL\n", + " location = 'data/2017-03-07/#021_testsweep_12-39-09'\n", + " | | | \n", + " Setpoint | qdac_ch41_v_set | ch41_v | (100,)\n", + " Setpoint | qdac_ch42_v_set | ch42_v | (100, 100)\n", + " Measured | Agilent_volt | volt | (100, 100)\n", + "started at 2017-03-07 12:44:33\n", + "\n", + "\n", + "Did a 100x100-point QCoDeS sweep of the QDac/Agilent pair in 323.8 s\n", + "\n", + "\n" + ] + } + ], + "source": [ + "# Perform the same sweep as before, but this time nested inside another sweep of the same length,\n", + "# i.e. at each point of the original sweep, another QDac channel sweeps its voltage in the same number of steps\n", + "\n", + "ks.display_text('QCoDeS')\n", + "agi.display_text('QCoDeS')\n", + "\n", + "N = 100\n", + "V1 = 0.2\n", + "V2 = 0.5\n", + "dV = (V2-V1)/(N-1)\n", + "\n", + "loop = qc.Loop(qdac.ch42_v.sweep(V1, V2, dV)).loop(qdac.ch41_v.sweep(V1, V2, dV)).each(ks.volt)\n", + "data = loop.get_data_set(name='testsweep')\n", + "t_start = time.time()\n", + "_ = loop.run()\n", + "t_stop = time.time()\n", + "print('\\n\\nDid a {}x{}-point QCoDeS sweep of the QDac/Keysight pair in {:.1f} s\\n\\n'.format(N, N, t_stop-t_start))\n", + "\n", + "loop = qc.Loop(qdac.ch41_v.sweep(V1, V2, dV)).loop(qdac.ch42_v.sweep(V1, V2, dV)).each(agi.volt)\n", + "data = loop.get_data_set(name='testsweep')\n", + "t_start = time.time()\n", + "_ = loop.run()\n", + "t_stop = time.time()\n", + "print('\\n\\nDid a {}x{}-point QCoDeS sweep of the QDac/Agilent pair in {:.1f} s\\n\\n'.format(N, N, t_stop-t_start))\n", + "\n", + "ks.display_clear()\n", + "agi.display_clear()" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "collapsed": true, + "deletable": true, + "editable": true + }, + "outputs": [], + "source": [ + "ks.close()\n", + "agi.close()\n", + "qdac.close()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true, + "deletable": true, + "editable": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/qcodes/__init__.py b/qcodes/__init__.py index b7d7676af239..1d40c8c3b31d 100644 --- a/qcodes/__init__.py +++ b/qcodes/__init__.py @@ -45,6 +45,7 @@ from qcodes.instrument.base import Instrument from qcodes.instrument.ip import IPInstrument from qcodes.instrument.visa import VisaInstrument +from qcodes.instrument.channel import InstrumentChannel, ChannelList from qcodes.instrument.function import Function from qcodes.instrument.parameter import ( diff --git a/qcodes/instrument_drivers/Keysight/Keysight_34465A.py b/qcodes/instrument_drivers/Keysight/Keysight_34465A.py new file mode 100644 index 000000000000..991288fb577e --- /dev/null +++ b/qcodes/instrument_drivers/Keysight/Keysight_34465A.py @@ -0,0 +1,400 @@ +# QCoDeS driver for the Keysight 34465A Digital Multimeter + +from functools import partial +import numpy as np +import logging + +from qcodes.instrument.parameter import ArrayParameter +import qcodes.utils.validators as vals +from qcodes import VisaInstrument + +log = logging.getLogger(__name__) + + +class ArrayMeasurement(ArrayParameter): + """ + Class to return several values. Really represents a measurement routine. + """ + + def __init__(self, name, shape=(1,), *args, **kwargs): + + super().__init__(name, shape=shape, *args, **kwargs) + + self.label = '' + self.unit = '' + self.properly_prepared = False + + def prepare(self): + """ + Prepare the measurement, create the setpoints. + + There is some randomness in the measurement times. + """ + + inst = self._instrument + + N = inst.sample_count() + + # ensure correct instrument settings + inst.aperture_mode('OFF') # aperture mode seems slower ON than OFF + inst.trigger_count(1) + inst.trigger_delay(0) + inst.sample_count_pretrigger(0) + inst.sample_source('TIM') + inst.autorange('OFF') + + if inst.trigger_source() is None: + raise ValueError('Trigger source unspecified! Please set ' + "trigger_source to 'INT' or 'EXT'.") + + # Final step + self.time_per_point = inst.sample_timer_minimum() + inst.sample_timer(self.time_per_point) + + self.setpoints = (tuple(np.linspace(0, N*self.time_per_point, N)),) + self.shape = (N,) + + self.properly_prepared = True + + def get(self): + + if not self.properly_prepared: + raise ValueError('ArrayMeasurement not properly_prepared. ' + 'Please run prepare().') + + N = self._instrument.sample_count() + log.debug("Acquiring {} samples.".format(N)) + + # Ensure that the measurement doesn't time out + # TODO (WilliamHPNielsen): What if we wait really long for a trigger? + old_timeout = self._instrument.visa_handle.timeout + self._instrument.visa_handle.timeout = N*1000*1.2*self.time_per_point + self._instrument.visa_handle.timeout += old_timeout + + # Turn off the display to increase measurement speed + self._instrument.display_text('Acquiring {} samples'.format(N)) + + self._instrument.init_measurement() + rawvals = self._instrument.ask('FETCH?') + + self._instrument.visa_handle.timeout = old_timeout + + # parse the acquired values + try: + numvals = np.array(list(map(float, rawvals.split(',')))) + except AttributeError: + numvals = None + + self._instrument.display_clear() + + return numvals + + +class Keysight_34465A(VisaInstrument): + """ + Instrument class for Keysight 34465A. + + + The driver is written such that usage with models + 34460A, 34461A, and 34470A should be seamless. + + Tested with: 34465A. + + The driver currently only supports using the instrument as a voltmeter. + + Attributes: + model (str): The model number of the instrument + NPLC_list (list): A list of the available Power Line Cycle settings + ranges (list): A list of the available voltage ranges + """ + + def __init__(self, name, address, DIG=False, utility_freq=50, silent=False, + **kwargs): + """ + Create an instance of the instrument. + + Args: + name (str): Name used by QCoDeS. Appears in the DataSet + address (str): Visa-resolvable instrument address. + utility_freq (int): The local utility frequency in Hz. Default: 50 + DIG (bool): Is the DIG option installed on the instrument? + Default: False. + silent (bool): If True, the connect_message of the instrument + is supressed. Default: False + Returns: + Keysight_34465A + """ + if utility_freq not in [50, 60]: + raise ValueError('Can not set utility frequency to ' + '{}. '.format(utility_freq) + + 'Please enter either 50 Hz or 60 Hz.') + + super().__init__(name, address, terminator='\n', **kwargs) + + idn = self.IDN.get() + self.model = idn['model'] + + #################################### + # Instrument specifications + + PLCs = {'34460A': [0.02, 0.2, 1, 10, 100], + '34461A': [0.02, 0.2, 1, 10, 100], + '34465A': [0.02, 0.06, 0.2, 1, 10, 100], + '34470A': [0.02, 0.06, 0.2, 1, 10, 100] + } + if DIG: + PLCs['34465A'] = [0.001, 0.002, 0.006] + PLCs['34465A'] + PLCs['34470A'] = [0.001, 0.002, 0.006] + PLCs['34470A'] + + ranges = {'34460A': [10**n for n in range(-3, 9)], # 1 m to 100 M + '34461A': [10**n for n in range(-3, 9)], # 1 m to 100 M + '34465A': [10**n for n in range(-3, 10)], # 1 m to 1 G + '34470A': [10**n for n in range(-3, 10)], # 1 m to 1 G + } + + # The resolution factor order matches the order of PLCs + res_factors = {'34460A': [300e-6, 100e-6, 30e-6, 10e-6, 3e-6], + '34461A': [100e-6, 10e-6, 3e-6, 1e-6, 0.3e-6], + '34465A': [3e-6, 1.5e-6, 0.7e-6, 0.3e-6, 0.1e-6, + 0.03e-6], + '34470A': [1e-6, 0.5e-6, 0.3e-6, 0.1e-6, 0.03e-6, + 0.01e-6] + } + if DIG: + res_factors['34465A'] = [30e6, 15e-6, 6e-6] + res_factors['34464A'] + res_factors['34470A'] = [30e-6, 10e-6, 3e-6] + res_factors['34470A'] + + # Define the extreme aperture time values for the 34465A and 34470A + if utility_freq == 50: + apt_times = {'34465A': [0.3e-3, 2], + '34470A': [0.3e-3, 2]} + if utility_freq == 60: + apt_times = {'34465A': [0.3e-3, 1.67], + '34470A': [0.3e-3, 1.67]} + if DIG: + apt_times['34465A'][0] = 20e-6 + apt_times['34470A'][0] = 20e-6 + + self._resolution_factors = res_factors[self.model] + self.ranges = ranges[self.model] + self.NPLC_list = PLCs[self.model] + self._apt_times = apt_times[self.model] + + #################################### + # PARAMETERS + + self.add_parameter('NPLC', + get_cmd='SENSe:VOLTage:DC:NPLC?', + get_parser=float, + set_cmd=self._set_NPLC, + vals=vals.Enum(*self.NPLC_list), + label='Integration time', + unit='NPLC') + + self.add_parameter('volt', + get_cmd=self._get_voltage, + label='Voltage', + unit='V') + + self.add_parameter('range', + get_cmd='SENSe:VOLTage:DC:RANGe?', + get_parser=float, + set_cmd='SENSe:VOLTage:DC:RANGe {:f}', + vals=vals.Enum(*self.ranges)) + + self.add_parameter('resolution', + get_cmd='SENSe:VOLTage:DC:RESolution?', + get_parser=float, + set_cmd=self._set_resolution, + label='Resolution', + unit='V') + + self.add_parameter('autorange', + label='Autorange', + set_cmd='SENSe:VOLtage:DC:RANGe:AUTO {}', + get_cmd='SENSe:VOLtage:DC:RANGe:AUTO?', + val_mapping={'ON': 1, 'OFF': 0}, + vals=vals.Enum('ON', 'OFF')) + + self.add_parameter('display_text', + label='Display text', + set_cmd='DISPLAY:TEXT "{}"', + get_cmd='DISPLAY:TEXT?', + vals=vals.Strings()) + + # TRIGGERING + + self.add_parameter('trigger_count', + label='Trigger Count', + set_cmd='TRIGger:COUNt {}', + get_cmd='TRIGger:COUNt?', + get_parser=float, + vals=vals.MultiType(vals.Numbers(1, 1e6), + vals.Enum('MIN', 'MAX', 'DEF', + 'INF'))) + + self.add_parameter('trigger_delay', + label='Trigger Delay', + set_cmd='TRIGger:DELay {}', + get_cmd='TRIGger:DELay?', + vals=vals.MultiType(vals.Numbers(0, 3600), + vals.Enum('MIN', 'MAX', 'DEF')), + get_parser=float) + + self.add_parameter('trigger_slope', + label='Trigger Slope', + set_cmd='TRIGger:SLOPe {}', + get_cmd='TRIGger:SLOPe?', + vals=vals.Enum('POS', 'NEG')) + + self.add_parameter('trigger_source', + label='Trigger Source', + set_cmd='TRIGger:SOURce {}', + get_cmd='TRIGger:SOURce?', + vals=vals.Enum('IMM', 'EXT', 'BUS', 'INT')) + + # SAMPLING + + self.add_parameter('sample_count', + label='Sample Count', + set_cmd=partial(self._set_databuffer_setpoints, + 'SAMPle:COUNt {}'), + get_cmd='SAMPle:COUNt?', + vals=vals.MultiType(vals.Numbers(1, 1e6), + vals.Enum('MIN', 'MAX', 'DEF')), + get_parser=int) + + self.add_parameter('sample_count_pretrigger', + label='Sample Pretrigger Count', + set_cmd='SAMPle:COUNt:PRETrigger {}', + get_cmd='SAMPle:COUNt:PRETrigger?', + vals=vals.MultiType(vals.Numbers(0, 2e6-1), + vals.Enum('MIN', 'MAX', 'DEF')), + get_parser=int, + docstring=('Allows collection of the data ' + 'being digitized the trigger. Reserves ' + 'memory for pretrigger samples up to the' + ' specified num. of pretrigger samples.') + ) + + self.add_parameter('sample_source', + label='Sample Timing Source', + set_cmd='SAMPle:SOURce {}', + get_cmd='SAMPle:SOURce?', + vals=vals.Enum('IMM', 'TIM'), + docstring=('Determines sampling time, immediate' + ' or using sample_timer')) + + self.add_parameter('sample_timer', + label='Sample Timer', + set_cmd='SAMPle:TIMer {}', + get_cmd='SAMPle:TIMer?', + unit='s', + vals=vals.MultiType(vals.Numbers(0, 3600), + vals.Enum('MIN', 'MAX', 'DEF')), + get_parser=float) + + self.add_parameter('sample_timer_minimum', + label='Minimal sample time', + get_cmd='SAMPle:TIMer? MIN', + get_parser=float, + unit='s') + + # The array parameter + self.add_parameter('data_buffer', + parameter_class=ArrayMeasurement) + + #################################### + # Model-specific parameters + + if self.model in ['34465A', '34470A']: + + self.add_parameter('aperture_mode', + label='Aperture mode', + set_cmd='SENSe:VOLTage:DC:APERture:ENABled {}', + get_cmd='SENSe:VOLTage:DC:APERture:ENABled?', + val_mapping={'ON': 1, 'OFF': 0}, + vals=vals.Enum('ON', 'OFF')) + + self.add_parameter('aperture_time', + label='Aperture time', + set_cmd=self._set_apt_time, + get_cmd='SENSe:VOLTage:DC:APERture?', + get_parser=float, + vals=vals.Numbers(*self._apt_times), + docstring=('Setting the aperture time ' + 'automatically enables the aperture' + ' mode.')) + + self.add_function('init_measurement', call_cmd='INIT') + self.add_function('reset', call_cmd='*RST') + self.add_function('display_clear', call_cmd=('DISPLay:TEXT:CLEar')) + + if not silent: + self.connect_message() + + def _get_voltage(self): + # TODO: massive improvements! + # The 'READ?' command will return anything the instrument is set up + # to return, i.e. not necessarily a voltage (might be current or + # or resistance) and not necessarily a single value. This function + # should be aware of the configuration. + + response = self.ask('READ?') + + return float(response) + + def _set_databuffer_setpoints(self, cmd, value): + """ + set_cmd for all databuffer-setpoint related parameters + """ + + self.data_buffer.properly_prepared = False + self.write(cmd.format(value)) + + def _set_apt_time(self, value): + self.write('SENSe:VOLTage:DC:APERture {:f}'.format(value)) + + # setting aperture time switches aperture mode ON + self.aperture_mode.get() + + def _set_NPLC(self, value): + self.write('SENSe:VOLTage:DC:NPLC {:f}'.format(value)) + + # This will change data_buffer setpoints (timebase) + self.data_buffer.properly_prepared = False + + # resolution settings change with NPLC + self.resolution.get() + + # setting NPLC switches off aperture mode + if self.model in ['34465A', '34470A']: + self.aperture_mode.get() + + def _set_range(self, value): + self.write('SENSe:VOLTage:DC:RANGe {:f}'.format(value)) + + # resolution settings change with range + + self.resolution.get() + + def _set_resolution(self, value): + rang = self.range.get() + + # convert both value*range and the resolution factors + # to strings with few digits, so we avoid floating point + # rounding errors. + res_fac_strs = ['{:.1e}'.format(v * rang) + for v in self._resolution_factors] + if '{:.1e}'.format(value) not in res_fac_strs: + raise ValueError( + 'Resolution setting {:.1e} ({} at range {}) ' + 'does not exist. ' + 'Possible values are {}'.format(value, value, rang, + res_fac_strs)) + + self.write('VOLT:DC:RES {:.1e}'.format(value)) + + # NPLC settings change with resolution + + self.NPLC.get() From fda0f8d0143065d8c739937d3ba010d302e62cf2 Mon Sep 17 00:00:00 2001 From: Sebastian Pauka Date: Tue, 11 Apr 2017 10:10:45 +1000 Subject: [PATCH 02/49] feat: Add channelization Initial commit of two new classes to allow for channelization of instruments. This includes ChannelList, a container for channels in an instrument, and InstrumentChannel which implements a base class for an instance of a channel. --- qcodes/instrument/channel.py | 300 +++++++++++++++++++++++++++++++++++ 1 file changed, 300 insertions(+) create mode 100644 qcodes/instrument/channel.py diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py new file mode 100644 index 000000000000..f4ca73e72438 --- /dev/null +++ b/qcodes/instrument/channel.py @@ -0,0 +1,300 @@ +""" Base class for the channel of an instrument """ + +from .base import Instrument +from .parameter import MultiParameter +from ..utils.metadata import Metadatable +from ..utils.helpers import full_class + +class InstrumentChannel(Instrument): + """ + Base class for a channel in an instrument + + Args: + parent (Instrument): the instrument to which this channel should be attached + + name (str): the name of this channel + + Attributes: + name (str): the name of this channel + + parameters (Dict[Parameter]): All the parameters supported by this channel. + Usually populated via ``add_parameter`` + + functions (Dict[Function]): All the functions supported by this channel. + Usually populated via ``add_function`` + """ + + def __init__(self, parent, name, **kwargs): + # Initialize base classes of Instrument. We will overwrite what we want to do + # in the Instrument initializer + super(Instrument, self).__init__(**kwargs) + + self.parameters = {} + self.functions = {} + + self.name = "{}_{}".format(parent.name, str(name)) + self._meta_attrs = ['name'] + + self._parent = parent + + def __repr__(self): + """Custom repr to give parent information""" + return '<{}: {} of {}: {}>'.format(type(self).__name__, self.name, + type(self._parent).__name__, self._parent.name) + + # We aren't a member of the global list of instruments, don't try and remove ourself + def __del__(self): + """ Does nothing for an instrument channel """ + pass + + def close(self): + """ Doesn't make sense to just close a channel by default, raise NotImplemented """ + raise NotImplemented("Can't close a channel. Close my parent instead.") + + @classmethod + def record_instance(cls, instance): + """ Instances should not be recorded for channels. This should happen for the parent instrument. """ + pass + + @classmethod + def instances(cls): + """ Instances should not be recorded for channels. This should happen for the parent instrument. """ + pass + + @classmethod + def remove_instances(cls, instance): + """ It doesn't make sense to remove a channel from an instrument, raise NotImplemented""" + raise NotImplemented("Can't remove a channel.") + + # This method doesn't make sense for a channel, raise NotImplemented + @classmethod + def find_instruments(cls, name, instrument_class=None): + raise NotImplemented("Can't find instruments in a channel") + + # Pass any commands to read or write from the instrument up to the parent + def write(self, cmd): + return self._parent.write(cmd) + def write_raw(self, cmd): + return self._parent.write_raw(cmd) + + def ask(self, cmd): + return self._parent.ask(cmd) + def ask_raw(self, cmd): + return self._parent.ask_raw(cmd) + +class ChannelList(Metadatable): + """ + Container for channelized parameters that allows for sweeps over all channels, as well + as addressing of individual channels. + + Args: + parent (Instrument): the instrument to which this channel should be attached + + name (string): the name of the channel list + + chan_type (InstrumentChannel): the type of channel contained within this list + + chan_list (Iterable[chan_type]): An optional iterable of channels of type chan_type. + This will create a list and immediately lock the ChannelList. + + Attributes: + parameters (Dict[Parameter]): All the parameters supported by this group of channels. + + functions (Dict[Function]): All the functions supported by this group of channels + """ + + def __init__(self, parent, name, chan_type, chan_list=None): + super().__init__() + + self._parent = parent + self._name = name + if type(chan_type) != type or not issubclass(chan_type, InstrumentChannel): + raise ValueError("Channel Lists can only hold instances of type InstrumentChannel") + self._chan_type = chan_type + + # If a list of channels is not provided, define a list to store channels. + # This will eventually become a locked tuple. + if chan_list is None: + self._locked = False + self._channels = [] + else: + self._locked = True + self._channels = tuple(chan_list) + if not all(isinstance(chan, chan_type) for chan in self._channels): + raise TypeError("All items in this channel list must be of type {}.".format(chan_type.__name__)) + + def __getitem__(self, i): + """ + Return either a single channel, or a new ChannelList containing only the specified channels + + Args: + i (int/slice): Either a single channel index or a slice of channels to get + """ + if isinstance(i, slice): + return ChannelList(self._parent, self._chan_type, self._channels[i]) + return self._channels[i] + + def __iter__(self): + return iter(self._channels) + + def __len__(self): + return len(self._channels) + + def __repr__(self): + return "ChannelList({!r}, {}, {!r})".format(self._parent, self._chan_type.__name__, self._channels) + + def __add__(self, other): + """ + Return a new channel list containing the channels from both ChannelList self and r. + + Both channel lists must hold the same type and have the same parent. + + Args: + other(ChannelList): Right argument to add. + """ + if not isinstance(self, ChannelList) or not isinstance(other, ChannelList): + raise TypeError("Can't add objects of type {} and {} together".format( + type(self).__name__, type(other).__name__)) + if self._chan_type != other._chan_type: + raise TypeError("Both l and r arguments to add must contain channels of the same type." + " Adding channels of type {} and {}.".format(self._chan_type.__name__, + other._chan_type.__name__)) + if self._parent != other._parent: + raise ValueError("Can only add channels from the same parent together.") + + return ChannelList(self._parent, self._chan_type, self._channels + other._channels) + + def append(self, object): + """ + When initially constructing the channel list, a new channel to add to the end of the list + + Args: + object(chan_type): New channel to add to the list. + """ + if self._locked: + raise AttributeError("Cannot append to a locked channel list") + if not isinstance(object, self._chan_type): + raise TypeError("All items in a channel list must be of the same type." + " Adding {} to a list of {}.".format(type(object).__name__, self._chan_type.__name__)) + return self._channels.append(object) + + def extend(self, objects): + """ + Insert an iterable of objects into the list of channels. + + Args: + objects(Iterable[chan_type]): A list of objects to add into the ChannelList. + """ + if self._locked: + raise AttributeError("Cannot extend a locked channel list") + if not all(isinstance(object, self._chan_type) for object in objects): + raise TypeError("All items in a channel list must be of the same type.") + return self._channels.extend(objects) + + def index(self, object): + """ + Return the index of the given object + + Args: + object(chan_type): The object to find in the channel list. + """ + return self._channels.index(object) + + def insert(self, index, object): + """ + Insert an object into the channel list at a specific index. + + Args: + index(int): Index to insert object. + + object(chan_type): Object of type chan_type to insert. + """ + if self._locked: + raise AttributeError("Cannot insert into a locked channel list") + if not isinstance(object, self._chan_type): + raise TypeError("All items in a channel list must be of the same type." + " Adding {} to a list of {}.".format(type(object).__name__, self._chan_type.__name__)) + return self._channels.insert(index, object) + + def lock(self): + """ + Lock the channel list. Once this is done, the channel list is converted to a tuple + and any future changes to the list are prevented. + """ + if self._locked: + return + self._channels = tuple(self._channels) + self._locked = True + + def snapshot_base(self, update=False): + """ + State of the instrument as a JSON-compatible dict. + + Args: + update (bool): If True, update the state by querying the + instrument. If False, just use the latest values in memory. + + Returns: + dict: base snapshot + """ + snap = {'channels': dict((chan.name, chan.snapshot(update=update)) + for chan in self._channels), + '__class__': full_class(self), + } + return snap + + def __getattr__(self, name): + """ + Return a multi-channel function or parameter that we can use to get or set all items + in a channel list simultaneously. + + Params: + name(str): The name of the parameter or function that we want to operate on. + """ + # Check if this is a valid parameter + if name in self._channels[0].parameters: + # We need to construct a MultiParameter object to get each of the + # values our of each parameter in our list + names = tuple("{}.{}".format(chan.name, name) for chan in self._channels) + shapes = tuple(() for chan in self._channels) #TODO: Pull shapes intelligently + labels = tuple(chan.parameters[name].label for chan in self._channels) + units = tuple(chan.parameters[name].unit for chan in self._channels) + + param = MultiChannelInstrumentParameter(self._channels, name, + name="Multi_{}".format(name), + names=names, shapes=shapes, instrument=self._parent, + labels=labels, units=units) + return param + + # Check if this is a valid function + if name in self._channels[0].functions: + # We want to return a reference to a function that would call the function + # for each of the channels in turn. + def multi_func(*args, **kwargs): + for chan in self._channels: + chan.functions[name](*args, **kwargs) + return multi_func + + raise AttributeError('\'{}\' object has no attribute \'{}\''.format(self.__class__.__name__, name)) + +class MultiChannelInstrumentParameter(MultiParameter): + """ + Parameter to get or set multiple channels simultaneously. + + Will normally be created by a ChannelList and not directly by anything else. + + Args: + channels(list[chan_type]): A list of channels which we can operate on simultaneously. + + param_name(str): Name of the multichannel parameter + """ + def __init__(self, channels, param_name, *args, **kwargs): + self._channels = channels + self._param_name = param_name + super().__init__(*args, **kwargs) + + def get(self): + """ + Return a tuple containing the data from each of the channels in the list + """ + return tuple(chan.parameters[self._param_name].get() for chan in self._channels) \ No newline at end of file From 863fa19d4fa63d14edc72381adebed313f2c860e Mon Sep 17 00:00:00 2001 From: Sebastian Pauka Date: Tue, 11 Apr 2017 10:11:34 +1000 Subject: [PATCH 03/49] feat: Add channelized Lakeshore Model336 Driver Add a driver for the Lakeshore Model 336 temperature controller using new channelization implementation. --- .../instrument_drivers/Lakeshore/Model_336.py | 52 +++++++++++++++++++ .../instrument_drivers/Lakeshore/__init__.py | 0 2 files changed, 52 insertions(+) create mode 100644 qcodes/instrument_drivers/Lakeshore/Model_336.py create mode 100644 qcodes/instrument_drivers/Lakeshore/__init__.py diff --git a/qcodes/instrument_drivers/Lakeshore/Model_336.py b/qcodes/instrument_drivers/Lakeshore/Model_336.py new file mode 100644 index 000000000000..2696781e3099 --- /dev/null +++ b/qcodes/instrument_drivers/Lakeshore/Model_336.py @@ -0,0 +1,52 @@ + +import numpy as numpy +from qcodes import Parameter, VisaInstrument, InstrumentChannel, ChannelList +from qcodes.utils.validators import Numbers, Enum, Strings + +class SensorChannel(InstrumentChannel): + """ + A single sensor channel of a temperature controller + """ + + _CHANNEL_VAL = Enum("A", "B", "C", "D") + + def __init__(self, parent, name, channel): + super().__init__(parent, name) + + # Validate the channel value + self._CHANNEL_VAL.validate(channel) + self._channel = channel # Channel on the temperature controller. Can be A-D + + # Add the various channel parameters + self.add_parameter('temperature', get_cmd='KRDG? {}'.format(self._channel), + get_parser=float, + label='Temerature', + unit='K') + self.add_parameter('sensor_raw', get_cmd='SRDG? {}'.format(self._channel), + get_parser=float, + label='Raw_Reading', + unit='Ohms') # TODO: This will vary based on sensor type + self.add_parameter('sensor_status', get_cmd='RDGST? {}'.format(self._channel), + val_mapping={'OK': 0, 'Invalid Reading': 1, 'Temp Underrange': 16, 'Temp Overrange': 32, + 'Sensor Units Zero': 64, 'Sensor Units Overrange': 128}, label='Sensor_Status') + + self.add_parameter('sensor_name', get_cmd='INNAME? {}'.format(self._channel), + get_parser=str, set_cmd='INNAME {},\"{{}}\"'.format(self._channel), vals=Strings(15), + label='Sensor_Name') + + +class Model_336(VisaInstrument): + """ + Lakeshore Model 336 Temperature Controller Driver + Controlled via sockets + """ + + def __init__(self, name, address, **kwargs): + super().__init__(name, address, terminator="\r\n", **kwargs) + + self.channels = ChannelList(self, "TempSensors", SensorChannel) + for chan in ('A', 'B', 'C', 'D'): + self.channels.append(SensorChannel(self, 'Chan{}'.format(chan), chan)) + self.channels.lock() + + self.connect_message() \ No newline at end of file diff --git a/qcodes/instrument_drivers/Lakeshore/__init__.py b/qcodes/instrument_drivers/Lakeshore/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 From 6914d9d9233427dc34923ca2033cee1371714ec9 Mon Sep 17 00:00:00 2001 From: Sebastian Pauka Date: Tue, 11 Apr 2017 10:20:42 +1000 Subject: [PATCH 04/49] feat: Add a channelized driver for the Harvard DAC Commits a channelized version of the Harvard DecaDAC driver using the new channelization implementation. Note that this driver at the present time also fixes a number of open bugs in the previous driver, including occasional crashes (#546), incorrectly reading DAC voltages (#563), reset (#478). This commit also adds a number of new features to the driver including: - Feature and version detection - Access to calibrated high-resolution mode of DAC - Parameter read from memory - Validation of writes --- qcodes/instrument_drivers/Harvard/Decadac.py | 653 +++++++++++++------ 1 file changed, 460 insertions(+), 193 deletions(-) diff --git a/qcodes/instrument_drivers/Harvard/Decadac.py b/qcodes/instrument_drivers/Harvard/Decadac.py index 0c3ef178749e..5d23f216acd2 100644 --- a/qcodes/instrument_drivers/Harvard/Decadac.py +++ b/qcodes/instrument_drivers/Harvard/Decadac.py @@ -1,259 +1,526 @@ -import logging -from time import sleep +from time import sleep, time from functools import partial -from qcodes.instrument.visa import VisaInstrument +from qcodes import VisaInstrument, InstrumentChannel, ChannelList, ManualParameter from qcodes.utils import validators as vals +from visa import constants as vi_const -log = logging.getLogger(__name__) +class DACException(Exception): + pass +class DacReader(object): + def _dac_parse(self, resp): + """ + Parses responses from the DAC. They should take the form of "!" + This command returns the value of resp. + """ + resp = resp.strip() + if resp[-1] != "!": + raise DACException("Unexpected terminator on response: {}. Should end with \"!\"".format(resp)) + return resp.strip()[1:-1] -class Decadac(VisaInstrument): - """ - The qcodes driver for the Decadac. - Each slot on the Deacadac is to be treated as a seperate - four-channel instrument. - - Tested with a Decadec firmware revion number 14081 (Decadac 139). - - The message strategy is the following: always keep the queue empty, so - that self.visa_handle.ask(XXX) will return the answer to XXX and not - some previous event. + def _dac_v_to_code(self, volt): + """ + Convert a voltage to the internal dac code (number between 0-65536) + based on the minimum/maximum values of a given channel. + Midrange is 32768. + """ + frac = (volt - self.min_val) / (self.max_val - self.min_val) + val = int(round(frac * 65536)) + if val >= 65536: # Check limits. For example setting max_val will cause an overflow + return 65535 + if val < 0: # Ensure no negative values + return 0 + return val + + def _dac_code_to_v(self, code): + """ + Convert a voltage to the internal dac code (number between 0-65536) + based on the minimum/maximum values of a given channel. + Midrange is 32768. + """ + frac = code/65536.0 + return (frac * (self.max_val - self.min_val)) + self.min_val + def _set_slot(self): + """ + Set the active DAC slot + """ + resp = self.ask_raw("B{};".format(self._slot)) + if int(self._dac_parse(resp)) != self._slot: + raise DACException("Unexpected return from DAC when setting slot: {}. DAC slot may not have been set.".format(response)) - Attributes: + def _set_channel(self): + """ + Set the active DAC channel + """ + resp = self.ask_raw("B{};C{};".format(self._slot, self._channel)) + if resp.strip() != "B{}!C{}!".format(self._slot, self._channel): + raise DACException("Unexpected return from DAC when setting channel: {}. DAC channel may not have been set.".format(response)) - _ramp_state (bool): If True, ramp state is ON. Default False. + def _query_address(self, addr, count=1, versa_eeprom=False): + """ + Query the value at the dac address given. - _ramp_time (int): The ramp time in ms. Default 100 ms. - """ + Args: + addr (int): The address to query. - def __init__(self, name, port, slot, timeout=2, baudrate=9600, - bytesize=8, **kwargs): + count (int): The number of bytes to query. + versa_eeprom(bool): do we want to read from the versadac (slot) EEPROM """ - - Creates an instance of the Decadac instrument corresponding to one slot - on the physical instrument. + # Check if we actually have anything to query + if count == 0: + return 0 + + # Validate address + addr = int(addr) + if addr < 0 or addr > 1107296266: + raise DACException("Invalid address {}.".format(addr)) + + # Choose a poke command depending on whether we are querying a + # VERSADAC eeprom or main memory + # If we are writing to a VERSADAC, we must also set the slot. + if versa_eeprom: + self._set_slot() + query_command = "e;" + else: + query_command = "p;" + + # Read a number of bytes from the device and convert to an int + val = 0 + for i in range(count): + # Set DAC to point to address + ret = int(self._dac_parse(self.ask_raw("A{};".format(addr)))) + if ret != addr: + raise DACException("Failed to set EEPROM address {}.".format(addr)) + val += int(self._dac_parse(self.ask_raw(query_command))) << (32*(count-i-1)) + addr += 1 + + return val + + def _write_address(self, addr, val, versa_eeprom=False): + """ + Write a value to a given DAC address Args: - name (str): What this instrument is called locally. + addr (int): The address to query. - port (number): The number (only) of the COM port to connect to. + val (int): The value to write. - slot (int): The slot to use. + versa_eeprom(bool): do we want to read from the versadac (slot) EEPROM + """ + # Validate address + addr = int(addr) + if addr < 0 or addr > 1107296266: + raise DACException("Invalid address {}.".format(addr)) + + # Validate value + val = int(val) + if val < 0 or val >= 2**32: + raise DACException("Writing invalid value ({}) to address {}.".format(val, addr)) + + # Choose a poke command depending on whether we are querying a + # VERSADAC eeprom or main memory. If we are writing to a versadac channel + # we must also set the slot + if versa_eeprom: + query_command = "e;" + write_command = "E" + self._set_slot() + else: + query_command = "p;" + write_command = "P" + + # Write the value to the DAC + # Set DAC to point to address + ret = int(self._dac_parse(self.ask_raw("A{};".format(addr)))) + if ret != addr: + raise DACException("Failed to set EEPROM address {}.".format(addr)) + self.ask_raw("{}{};".format(write_command, val)) + # Check the write was successful + if int(self._dac_parse(self.ask_raw(query_command))) != val: + raise DACException("Failed to write value ({}) to address {}.".format(val, addr)) + +class DacChannel(InstrumentChannel, DacReader): + """ + A single DAC channel of the DECADAC + """ + _CHANNEL_VAL = vals.Ints(0, 3) + + def __init__(self, parent, name, channel, min_val=-5, max_val=5): + super().__init__(parent, name) + + # Validate slot and channel values + self._CHANNEL_VAL.validate(channel) + self._channel = channel + self._slot = self._parent._slot + + # Calculate base address for querying channel parameters + # Note that the following values can be found using these offsets + # 0: Interrupt Period + # 4: DAC High Limit + # 5: DAC Low Limit + # 6: Slope (double) + # 8: DAC Value (double) + self._base_addr = 1536 + (16*4)*self._slot + 16*self._channel + + # Store min/max voltages + assert(min_val < max_val) + self.min_val = min_val + self.max_val = max_val + + # Add channel parameters + # Note we will use the older addresses to read the value from the dac rather than the newer + # 'd' command for backwards compatibility + self._volt_val = vals.Numbers(self.min_val, self.max_val) + self.add_parameter("volt", get_cmd=partial(self._query_address, self._base_addr+9, 1), + get_parser=self._dac_code_to_v, + set_cmd=self._set_dac, set_parser=self._dac_v_to_code, vals=self._volt_val, + label="Voltage", unit="V") + # The limit commands are used to sweep dac voltages. They are not safety features. + self.add_parameter("lower_ramp_limit", get_cmd=partial(self._query_address, self._base_addr+5), + get_parser=self._dac_code_to_v, + set_cmd="L{};", set_parser=self._dac_v_to_code, vals=self._volt_val, + label="Lower_Ramp_Limit", unit="V") + self.add_parameter("upper_ramp_limit", get_cmd=partial(self._query_address, self._base_addr+4), + get_parser=self._dac_code_to_v, + set_cmd="U{};", set_parser=self._dac_v_to_code, vals=self._volt_val, + label="Upper_Ramp_Limit", unit="V") + self.add_parameter("update_period", get_cmd=partial(self._query_address, self._base_addr), + get_parser=int, set_cmd="T{};", set_parser=int, vals=vals.Ints(50, 65535), + label="Update_Period", unit="us") + self.add_parameter("slope", get_cmd=partial(self._query_address, self._base_addr+6, 2), + get_parser=int, set_cmd="S{};", set_parser=int, vals=vals.Ints(-(2**32), 2**32), + label="Ramp_Slope") + + # Manual parameters to control whether DAC channels should ramp to voltages or jump + self._ramp_val = vals.Numbers(0, 10) + self.add_parameter("enable_ramp", parameter_class=ManualParameter, initial_value=False, + vals=vals.Bool()) + self.add_parameter("ramp_rate", parameter_class=ManualParameter, initial_value=0.1, + vals=self._ramp_val, unit="V/s") + + # Add ramp function to the list of functions + self.add_function("ramp", call_cmd=self._ramp, args=(self._volt_val, self._ramp_val)) + + # If we have access to the VERSADAC (slot) EEPROM, we can set the inital + # value of the channel. + # NOTE: these values will be overwritten by a K3 calibration + if self._parent._VERSA_EEPROM_available: + _INITIAL_ADDR = [6, 8, 32774, 32776] + self.add_parameter("initial_value", + get_cmd=partial(self._query_address, _INITIAL_ADDR[self._channel], versa_eeprom=True), + get_parser=self._dac_code_to_v, + set_cmd=partial(self._write_address, _INITIAL_ADDR[self._channel], versa_eeprom=True), + set_parser=set._dac_v_to_code, vals=vals.Numbers(self.min_val, self.max_val)) + + def _ramp(self, val, rate, block=True): + """ + Ramp the DAC to a given voltage. - timeout (number): Seconds to wait for message response. - Default 0.3. + Params: + val (float): The voltage to ramp to in volts - baudrate (number): The connection baudrate. Default 9600. + rate (float): The ramp rate in units of volts/s - bytesize (number): The connection bytesize. Default 8. + block (bool): Should the call block until the ramp is complete? """ - address = 'ASRL{:d}::INSTR'.format(port) - self.slot = slot + # We need to know the current dac value (in raw units), as well as the update rate + c_volt = self.volt.get() # Current Voltage + if c_volt == val: # If we are already at the right voltage, we don't need to ramp + return + c_val = self._dac_v_to_code(c_volt) # Current voltage in DAC units + e_val = self._dac_v_to_code(val) # Endpoint in DAC units + t_rate = 1/(self.update_period.get() * 1e-6) # Number of refreshes per second + secs = abs((c_volt - val)/rate) # Number of seconds to ramp + + # The formula to calculate the slope is: Number of DAC steps divided by the number of time + # steps in the ramp multiplied by 65536 + slope = int(((e_val - c_val)/(t_rate*secs))*65536) + + # Now let's set up our limits and ramo slope + if slope > 0: + self.upper_ramp_limit.set(val) + else: + self.lower_ramp_limit.set(val) + self.slope.set(slope) + + # Block until the ramp is complete is block is True + if block: + while self.slope.get() != 0: + pass - super().__init__(name, address, timeout=timeout, **kwargs) + def _set_dac(self, code): + """ + Set the voltage on the dac channel, ramping if the enable_rate parameter is set for this + channel. - # set instrument operation state variables - self._ramp_state = False - self._ramp_time = 100 - self._voltranges = [1, 1, 1, 1] - self._offsets = [0, 0, 0, 0] + Params: + code (int): the DAC code to set the voltage to + """ + if self.enable_ramp.get(): + self._ramp(self._dac_code_to_v(code), rate=self.ramp_rate.get()) + else: + code = int(code) + self._set_channel() + self.ask_raw("U65535;L0;D{};".format(code)) + + def write(self, cmd): + """ + Overload write to set channel prior to any channel operations. + Since all commands are echoed back, we must keep track of responses + as well, otherwise commands receive the wrong response. + """ + self._set_channel() + return self.ask_raw(cmd) - # channels - for channelno in range(4): - self.add_parameter('ch{}_voltage'.format(channelno), - get_cmd=partial(self._getvoltage, - channel=channelno), - set_cmd=partial(self._setvoltage, - channel=channelno), - label='Voltage', - unit='V') + def ask(self, cmd): + """ + Overload ask to set channel prior to operations + """ + self._set_channel() + return self.ask_raw(cmd) - self.add_parameter('ch{}_voltrange'.format(channelno), - get_cmd=partial(self._getvoltrange, channelno), - set_cmd=partial(self._setvoltrange, channelno), - vals=vals.Enum(1, 2, 3)) +class DacSlot(InstrumentChannel, DacReader): + """ + A single DAC Slot of the DECADAC + """ + _SLOT_VAL = vals.Ints(0, 5) + + def __init__(self, parent, name, slot, min_val=-5, max_val=5): + super().__init__(parent, name) + + # Validate slot and channel values + self._SLOT_VAL.validate(slot) + self._slot = slot + + # Store whether we have access to the VERSADAC EEPROM + self._VERSA_EEPROM_available = self._parent._VERSA_EEPROM_available + + # Create a list of channels in the slot + self.channels = ChannelList(self, "Slot_Channels", DacChannel) + for i in range(4): + self.channels.append(DacChannel(self, "Chan{}".format(i), i)) + + # Set the slot mode. Valid modes are: + # Off: Channel outputs are disconnected from the input, grounded with 10MOhm. + # Fine: 2-channel mode. Channels 0 and 1 are output, use 2 and 3 for fine + # adjustment of Channels 0 and 1 respectively + # Coarse: All 4 channels are used as output + # FineCald: Calibrated 2-channel mode, with 0 and 1 output, 2 and 3 used + # automatically for fine adjustment. This mode only works for calibrated + # DecaDAC's + # Unfortunately there is no known way of reading the slot mode hence this will be + # set in initialization + if self._parent._cal_supported: + slot_modes = {"Off": 0, "Fine": 1, "Coarse": 2, "FineCald": 3} + else: + slot_modes = {"Off": 0, "Fine": 1, "Coarse": 2} + self.add_parameter('slot_mode', get_cmd="m;", get_parser=self._dac_parse, set_cmd="M{};", + val_mapping=slot_modes) + + # Enable all slots in coarse mode. + self.slot_mode.set("Coarse") + + def write(self, cmd): + """ + Overload write to set channel prior to any channel operations. + Since all commands are echoed back, we must keep track of responses + as well, otherwise commands receive the wrong response. + """ + self._set_slot() + return self.ask_raw(cmd) - self.add_parameter('ch{}_offset'.format(channelno), - get_cmd=partial(self._getoffset, channelno), - set_cmd=partial(self._setoffset, channelno), - label='Channel {} offset'.format(channelno), - unit='V', - docstring=""" - The offset is applied to the channel. - E.g. if ch1_offset = 1 and ch_voltage - is set to 1, the instrument is told to - output 2 volts. - """) + def ask(self, cmd): + """ + Overload ask to set channel prior to operations + """ + self._set_slot() + return self.ask_raw(cmd) - self.add_parameter('mode', - label='Output mode', - set_cmd='B {}; M {};'.format(self.slot, '{}'), - vals=vals.Enum(0, 1), - docstring=""" - The operational mode of the slot. - 0: output off, 1: output on. - """) +class Decadac(VisaInstrument, DacReader): + """ + The qcodes driver for the Decadac. + Each slot on the Deacadac is to be treated as a seperate + four-channel instrument. + + Tested with a Decadec firmware revion number 14081 (Decadac 139). - # initialise hardware settings - self.mode.set(1) + The message strategy is the following: always keep the queue empty, so + that self.visa_handle.ask(XXX) will return the answer to XXX and not + some previous event. - def _getoffset(self, n): - return self._offsets[n] - def _setoffset(self, n, val): - self._offsets[n] = val + Attributes: - def _getvoltrange(self, n): - return self._voltranges[n] + _ramp_state (bool): If True, ramp state is ON. Default False. - def _setvoltrange(self, n, val): - self._voltranges[n] = val + _ramp_time (int): The ramp time in ms. Default 100 ms. + """ - def _getvoltage(self, channel): - """ - Function to query the voltage. Flushes the message queue in that - process. + def __init__(self, name, address, min_val=-5, max_val=5, **kwargs): """ - # set the relevant channel and slot to query - mssg = 'B {:d}; C {:d};'.format(self.slot, channel) - mssg += 'd;' + Creates an instance of the Decadac instrument corresponding to one slot + on the physical instrument. - # a bit of string juggling to extract the voltage - rawresponse = self.visa_handle.ask(mssg) - temp = rawresponse[::-1] - temp = temp[3:temp.upper().find('D')-1] - response = temp[::-1] + Args: + name (str): What this instrument is called locally. - rawvoltage = self._code2voltage(response, channel) - actualvoltage = rawvoltage - self._offsets[channel] + port (str): The address of the DAC. For a serial port this is ASRLn::INSTR + where n is replaced with the address set in the VISA control panel. + Baud rate and other serial parameters must also be set in the VISA control + panel. - return actualvoltage + min_val (number): The minimum value in volts that can be output by the DAC. + This value should correspond to the DAC code 0. - def _setvoltage(self, voltage, channel): - """ - Function to set the voltage. Depending on whether self._ramp_state is - True or False, this function either ramps from the current voltage to - the specified voltage or directly makes the voltage jump there. + max_val (number): The maximum value in volts that can be output by the DAC. + This value should correspond to the DAC code 65536. - Args: - voltage (number): the set voltage. """ - actualvoltage = voltage + self._offsets[channel] - code = self._voltage2code(actualvoltage, channel) - - mssg = 'B {:d}; C {:d};'.format(self.slot, channel) + super().__init__(name, address, **kwargs) - if not self._ramp_state: - mssg += 'D ' + code + ';' + # Flush the input and output buffers, in case there is any remaining junk + self.visa_handle.flush(vi_const.VI_READ_BUF_DISCARD | vi_const.VI_WRITE_BUF_DISCARD) - self.visa_handle.write(mssg) + # Do feature detection + self._feature_detect() - # due to a quirk of the Decadac, we spare the user of an error - # sometimes encountered on first run - try: - self.visa_handle.read() - except UnicodeDecodeError: - log.warning(" Decadac returned nothing and possibly did nothing. " + - "Please re-run the command") - pass + # Create channels + self.channels = ChannelList(self, "Channels", DacChannel) + self.slots = ChannelList(self, "Slots", DacSlot) + for i in range(6): # Create the 6 DAC slots + self.slots.append(DacSlot(self, "Slot{}".format(i), i, min_val, max_val)) + self.channels.extend(self.slots[i].channels) + self.slots.lock() + self.channels.lock() - if self._ramp_state: - currentcode = self._voltage2code(self._getvoltage(channel), - channel) - slope = int((float(code)-float(currentcode)) / - (10*self._ramp_time)*2**16) - if slope < 0: - limit = 'L' - else: - limit = 'U' - - script = ['{', - '*1:', - 'M2;', - 'T 100;', # 1 timestep: 100 micro s - limit + code + ';', - 'S' + str(slope) + ';', - 'X0;', - '}'] - runcmd = 'X 1;' - mssg += ''.join(script) + runcmd - self.visa_handle.write(mssg) - sleep(0.0015*self._ramp_time) # Required sleep. - self.visa_handle.read() - - # reset channel voltage ranges - if slope < 0: - self.visa_handle.write('L 0;') - self.visa_handle.read() - else: - self.visa_handle.write('U 65535;') - self.visa_handle.read() + # Custom connect message, since we can't identify + t = time() - (self._t0) + print("Connected to Harvard DecaDAC (hw ver: {}, serial: {}) in {:.2f}s".format( + self.version, self.serial_no, t)) - def set_ramping(self, state, time=None): + def set_all(self, volt): """ - Function to set _ramp_state and _ramp_time. + Set all dac channels to a specific voltage. If channels are set to ramp then the ramps + will occur in sequence, not simultaneously. Args: - state (bool): True sets ramping ON. - - time (Optiona[int]): the ramp time in ms + volt(float): The voltage to set all gates to. """ - self._ramp_state = state - if time is not None: - self._ramp_time = time + for chan in self.channels: + chan.volt.set(volt) - def get_ramping(self): + def ramp_all(self, volt, ramp_rate): """ - Queries the value of self._ramp_state and self._ramp_time. + Ramp all dac channels to a specific voltage at the given rate simultaneously. Note + that the ramps are not synchronized due. - Returns: - str: ramp state information - """ - switch = {True: 'ON', - False: 'OFF'} - mssg = 'Ramp state: ' + switch[self._ramp_state] - mssg += '. Ramp time: {:d} ms.'.format(int(self._ramp_time)) - return mssg + Args: + volt(float): The voltage to ramp all channels to. - def _code2voltage(self, code, channel): + ramp_rate(float): The rate in volts per second to ramp """ - Helper function translating a 32 bit code used internally by - the Decadac into a voltage. - - Args: - code (str): The code string from the instrument. + # Start all channels ramping + for chan in self.channels: + chan._ramp(volt, ramp_rate, block=False) + + # Wait for all channels to complete ramping. + # The slope is reset to 0 once ramping is complete. + for chan in self.channels: + while chan.slope.get(): + pass - channel (int): The relevant channel. + def get_idn(self): """ + Attempt to identify the dac. Since we don't have standard SCPI commands, *IDN will + do nothing on this DAC. - code = float(code) - translationdict = {1: lambda x: (x+1)*20/2**16-10, - 2: lambda x: (x+1)*10/2**16, - 3: lambda x: (x+1)*10/2**16-10} + Returns: + A dict containing a serial and hardware version + """ + self._feature_detect() - return translationdict[self._voltranges[channel]](code) + return {serial: self.serial_no, hardware_version: self.version} - def _voltage2code(self, voltage, channel): + def connect_message(self, idn_param='IDN', begin_time=None): """ - Helper function translating a voltage in V into a 32 bit code used - internally by the Decadac. + Print a connect message, taking into account the lack of a standard *IDN on + the Harvard DAC Args: - voltage (float): The physical voltage. - - channel (int): The relevant channel. - - Returns: - code (str): The corresponding voltage code. + begin_time (number): time.time() when init started. + Default is self._t0, set at start of Instrument.__init__. + """ + # start with an empty dict, just in case an instrument doesn't + # heed our request to return all 4 fields. + t = time.time() - (begin_time or self._t0) + + con_msg = ("Connected to Harvard DecaDAC " + "(hw ver: {}, serial: {}) in {:.2f}s".format( + self.version, self.serial_no, t)) + print(con_msg) + + def __repr__(self): + """Simplified repr giving just the class and name.""" + return '<{}: {}>'.format(type(self).__name__, self.name) + + def __del__(self): + """Close the instrument and remove its instance record.""" + try: + wr = weakref.ref(self) + if wr in getattr(self, '_instances', []): + self._instances.remove(wr) + self.close() + except: + pass + + def _feature_detect(self): + """ + Detect which features are available on the DAC by querying various parameters. """ - translationdict = {1: lambda x: 2**16/20*(x-2**-16+10), - 2: lambda x: 2**16/10*(x-2**-16), - 3: lambda x: 2**16/10*(x-2**-16+10)} - voltage_float = translationdict[self._voltranges[channel]](voltage) - return str(int(voltage_float)) + # Check whether EEPROM is installed + try: + if self._query_address(1107296256) == 21930: + self._EEPROM_available = True + else: + self._EEPROM_available = False + except DACException: + self._EEPROM_available = False + + # Check whether we can set startup values for the DAC. + # This requires access to the EEPROM on each slot + try: + # Let's temporarily pretend to be slot 0 + self._slot = 0 + self._query_address(6, versa_eeprom=True) + del self._slot + except DACException: + self._VERSA_EEPROM_available = False + + # Check whether calibration is supported + try: + if self._dac_parse(self.ask("k;")): + self._cal_supported = True + except DACException: + self._cal_supported = False + + # Finally try and read the DAC version and S/N. This is only possible if the EEPROM + # is queryable. + if self._EEPROM_available: + self.version = self._query_address(1107296266) + self.serial_no = self._query_address(1107296264) + else: + self.version = 0 + self.serial_no = 0 + + def write(self, cmd): + """ + Since all commands are echoed back, we must keep track of responses + as well, otherwise commands receive the wrong response. Hence + all writes must also read a response. + """ + return self.ask(cmd) \ No newline at end of file From 76af2a377c852af7ef90ea8fca42d9f26e3e10bb Mon Sep 17 00:00:00 2001 From: Sebastian Pauka Date: Tue, 11 Apr 2017 11:18:20 +1000 Subject: [PATCH 05/49] fix: bug in slicing and adding code Fix bug in slicing/adding code, not correctly passing along channel list name. --- qcodes/instrument/channel.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index f4ca73e72438..539b518fb58e 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -109,6 +109,7 @@ def __init__(self, parent, name, chan_type, chan_list=None): self._parent = parent self._name = name if type(chan_type) != type or not issubclass(chan_type, InstrumentChannel): + print(chan_type, InstrumentChannel) raise ValueError("Channel Lists can only hold instances of type InstrumentChannel") self._chan_type = chan_type @@ -131,7 +132,7 @@ def __getitem__(self, i): i (int/slice): Either a single channel index or a slice of channels to get """ if isinstance(i, slice): - return ChannelList(self._parent, self._chan_type, self._channels[i]) + return ChannelList(self._parent, self._name, self._chan_type, self._channels[i]) return self._channels[i] def __iter__(self): @@ -162,7 +163,7 @@ def __add__(self, other): if self._parent != other._parent: raise ValueError("Can only add channels from the same parent together.") - return ChannelList(self._parent, self._chan_type, self._channels + other._channels) + return ChannelList(self._parent, self._name, self._chan_type, self._channels + other._channels) def append(self, object): """ From 4f36e59b3612640d36a51c11b5a2ef0df3d61959 Mon Sep 17 00:00:00 2001 From: Sebastian Pauka Date: Tue, 11 Apr 2017 16:12:29 +1000 Subject: [PATCH 06/49] style: fix formatting and make codacy fixes --- qcodes/instrument/channel.py | 42 ++++++++++---------- qcodes/instrument_drivers/Harvard/Decadac.py | 21 +++++----- 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index 539b518fb58e..d5481d908523 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -27,7 +27,7 @@ class InstrumentChannel(Instrument): def __init__(self, parent, name, **kwargs): # Initialize base classes of Instrument. We will overwrite what we want to do # in the Instrument initializer - super(Instrument, self).__init__(**kwargs) + super().__init__(name=name, **kwargs) self.parameters = {} self.functions = {} @@ -49,7 +49,7 @@ def __del__(self): def close(self): """ Doesn't make sense to just close a channel by default, raise NotImplemented """ - raise NotImplemented("Can't close a channel. Close my parent instead.") + raise NotImplementedError("Can't close a channel. Close my parent instead.") @classmethod def record_instance(cls, instance): @@ -64,12 +64,12 @@ def instances(cls): @classmethod def remove_instances(cls, instance): """ It doesn't make sense to remove a channel from an instrument, raise NotImplemented""" - raise NotImplemented("Can't remove a channel.") + raise NotImplementedError("Can't remove a channel.") # This method doesn't make sense for a channel, raise NotImplemented @classmethod def find_instruments(cls, name, instrument_class=None): - raise NotImplemented("Can't find instruments in a channel") + raise NotImplementedError("Can't find instruments in a channel") # Pass any commands to read or write from the instrument up to the parent def write(self, cmd): @@ -108,12 +108,12 @@ def __init__(self, parent, name, chan_type, chan_list=None): self._parent = parent self._name = name - if type(chan_type) != type or not issubclass(chan_type, InstrumentChannel): + if not isinstance(chan_type, type) != type or not issubclass(chan_type, InstrumentChannel): print(chan_type, InstrumentChannel) raise ValueError("Channel Lists can only hold instances of type InstrumentChannel") self._chan_type = chan_type - # If a list of channels is not provided, define a list to store channels. + # If a list of channels is not provided, define a list to store channels. # This will eventually become a locked tuple. if chan_list is None: self._locked = False @@ -158,14 +158,14 @@ def __add__(self, other): type(self).__name__, type(other).__name__)) if self._chan_type != other._chan_type: raise TypeError("Both l and r arguments to add must contain channels of the same type." - " Adding channels of type {} and {}.".format(self._chan_type.__name__, + " Adding channels of type {} and {}.".format(self._chan_type.__name__, other._chan_type.__name__)) if self._parent != other._parent: raise ValueError("Can only add channels from the same parent together.") return ChannelList(self._parent, self._name, self._chan_type, self._channels + other._channels) - def append(self, object): + def append(self, obj): """ When initially constructing the channel list, a new channel to add to the end of the list @@ -174,10 +174,10 @@ def append(self, object): """ if self._locked: raise AttributeError("Cannot append to a locked channel list") - if not isinstance(object, self._chan_type): + if not isinstance(obj, self._chan_type): raise TypeError("All items in a channel list must be of the same type." - " Adding {} to a list of {}.".format(type(object).__name__, self._chan_type.__name__)) - return self._channels.append(object) + " Adding {} to a list of {}.".format(type(obj).__name__, self._chan_type.__name__)) + return self._channels.append(obj) def extend(self, objects): """ @@ -192,16 +192,16 @@ def extend(self, objects): raise TypeError("All items in a channel list must be of the same type.") return self._channels.extend(objects) - def index(self, object): + def index(self, obj): """ Return the index of the given object Args: object(chan_type): The object to find in the channel list. """ - return self._channels.index(object) + return self._channels.index(obj) - def insert(self, index, object): + def insert(self, index, obj): """ Insert an object into the channel list at a specific index. @@ -212,10 +212,10 @@ def insert(self, index, object): """ if self._locked: raise AttributeError("Cannot insert into a locked channel list") - if not isinstance(object, self._chan_type): + if not isinstance(obj, self._chan_type): raise TypeError("All items in a channel list must be of the same type." - " Adding {} to a list of {}.".format(type(object).__name__, self._chan_type.__name__)) - return self._channels.insert(index, object) + " Adding {} to a list of {}.".format(type(obj).__name__, self._chan_type.__name__)) + return self._channels.insert(index, obj) def lock(self): """ @@ -254,7 +254,7 @@ def __getattr__(self, name): """ # Check if this is a valid parameter if name in self._channels[0].parameters: - # We need to construct a MultiParameter object to get each of the + # We need to construct a MultiParameter object to get each of the # values our of each parameter in our list names = tuple("{}.{}".format(chan.name, name) for chan in self._channels) shapes = tuple(() for chan in self._channels) #TODO: Pull shapes intelligently @@ -262,8 +262,8 @@ def __getattr__(self, name): units = tuple(chan.parameters[name].unit for chan in self._channels) param = MultiChannelInstrumentParameter(self._channels, name, - name="Multi_{}".format(name), - names=names, shapes=shapes, instrument=self._parent, + name="Multi_{}".format(name), + names=names, shapes=shapes, instrument=self._parent, labels=labels, units=units) return param @@ -290,9 +290,9 @@ class MultiChannelInstrumentParameter(MultiParameter): param_name(str): Name of the multichannel parameter """ def __init__(self, channels, param_name, *args, **kwargs): + super().__init__(*args, **kwargs) self._channels = channels self._param_name = param_name - super().__init__(*args, **kwargs) def get(self): """ diff --git a/qcodes/instrument_drivers/Harvard/Decadac.py b/qcodes/instrument_drivers/Harvard/Decadac.py index 5d23f216acd2..26409ccd387d 100644 --- a/qcodes/instrument_drivers/Harvard/Decadac.py +++ b/qcodes/instrument_drivers/Harvard/Decadac.py @@ -1,4 +1,4 @@ -from time import sleep, time +from time import time from functools import partial from qcodes import VisaInstrument, InstrumentChannel, ChannelList, ManualParameter from qcodes.utils import validators as vals @@ -172,16 +172,16 @@ def __init__(self, parent, name, channel, min_val=-5, max_val=5): # Note we will use the older addresses to read the value from the dac rather than the newer # 'd' command for backwards compatibility self._volt_val = vals.Numbers(self.min_val, self.max_val) - self.add_parameter("volt", get_cmd=partial(self._query_address, self._base_addr+9, 1), + self.add_parameter("volt", get_cmd=partial(self._query_address, self._base_addr+9, 1), get_parser=self._dac_code_to_v, set_cmd=self._set_dac, set_parser=self._dac_v_to_code, vals=self._volt_val, label="Voltage", unit="V") # The limit commands are used to sweep dac voltages. They are not safety features. - self.add_parameter("lower_ramp_limit", get_cmd=partial(self._query_address, self._base_addr+5), + self.add_parameter("lower_ramp_limit", get_cmd=partial(self._query_address, self._base_addr+5), get_parser=self._dac_code_to_v, set_cmd="L{};", set_parser=self._dac_v_to_code, vals=self._volt_val, label="Lower_Ramp_Limit", unit="V") - self.add_parameter("upper_ramp_limit", get_cmd=partial(self._query_address, self._base_addr+4), + self.add_parameter("upper_ramp_limit", get_cmd=partial(self._query_address, self._base_addr+4), get_parser=self._dac_code_to_v, set_cmd="U{};", set_parser=self._dac_v_to_code, vals=self._volt_val, label="Upper_Ramp_Limit", unit="V") @@ -207,7 +207,7 @@ def __init__(self, parent, name, channel, min_val=-5, max_val=5): # NOTE: these values will be overwritten by a K3 calibration if self._parent._VERSA_EEPROM_available: _INITIAL_ADDR = [6, 8, 32774, 32776] - self.add_parameter("initial_value", + self.add_parameter("initial_value", get_cmd=partial(self._query_address, _INITIAL_ADDR[self._channel], versa_eeprom=True), get_parser=self._dac_code_to_v, set_cmd=partial(self._write_address, _INITIAL_ADDR[self._channel], versa_eeprom=True), @@ -304,10 +304,10 @@ def __init__(self, parent, name, slot, min_val=-5, max_val=5): # Set the slot mode. Valid modes are: # Off: Channel outputs are disconnected from the input, grounded with 10MOhm. - # Fine: 2-channel mode. Channels 0 and 1 are output, use 2 and 3 for fine + # Fine: 2-channel mode. Channels 0 and 1 are output, use 2 and 3 for fine # adjustment of Channels 0 and 1 respectively # Coarse: All 4 channels are used as output - # FineCald: Calibrated 2-channel mode, with 0 and 1 output, 2 and 3 used + # FineCald: Calibrated 2-channel mode, with 0 and 1 output, 2 and 3 used # automatically for fine adjustment. This mode only works for calibrated # DecaDAC's # Unfortunately there is no known way of reading the slot mode hence this will be @@ -368,7 +368,7 @@ def __init__(self, name, address, min_val=-5, max_val=5, **kwargs): name (str): What this instrument is called locally. port (str): The address of the DAC. For a serial port this is ASRLn::INSTR - where n is replaced with the address set in the VISA control panel. + where n is replaced with the address set in the VISA control panel. Baud rate and other serial parameters must also be set in the VISA control panel. @@ -416,7 +416,8 @@ def set_all(self, volt): def ramp_all(self, volt, ramp_rate): """ Ramp all dac channels to a specific voltage at the given rate simultaneously. Note - that the ramps are not synchronized due. + that the ramps are not synchronized due to communications time and DAC ramps starting + as soon as the commands are in. Args: volt(float): The voltage to ramp all channels to. @@ -458,7 +459,7 @@ def connect_message(self, idn_param='IDN', begin_time=None): # heed our request to return all 4 fields. t = time.time() - (begin_time or self._t0) - con_msg = ("Connected to Harvard DecaDAC " + con_msg = ("Connected to Harvard DecaDAC " "(hw ver: {}, serial: {}) in {:.2f}s".format( self.version, self.serial_no, t)) print(con_msg) From a79381960b6bfef29e900ffb796cdb7397a36159 Mon Sep 17 00:00:00 2001 From: Sebastian Pauka Date: Tue, 11 Apr 2017 17:47:39 +1000 Subject: [PATCH 07/49] style: fix more code style issues - Removed useless methods - Whitespace --- qcodes/instrument_drivers/Harvard/Decadac.py | 13 ++----------- qcodes/instrument_drivers/Lakeshore/Model_336.py | 8 +++----- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/qcodes/instrument_drivers/Harvard/Decadac.py b/qcodes/instrument_drivers/Harvard/Decadac.py index 26409ccd387d..72732902fdf6 100644 --- a/qcodes/instrument_drivers/Harvard/Decadac.py +++ b/qcodes/instrument_drivers/Harvard/Decadac.py @@ -8,7 +8,8 @@ class DACException(Exception): pass class DacReader(object): - def _dac_parse(self, resp): + @staticmethod + def _dac_parse(resp): """ Parses responses from the DAC. They should take the form of "!" This command returns the value of resp. @@ -468,16 +469,6 @@ def __repr__(self): """Simplified repr giving just the class and name.""" return '<{}: {}>'.format(type(self).__name__, self.name) - def __del__(self): - """Close the instrument and remove its instance record.""" - try: - wr = weakref.ref(self) - if wr in getattr(self, '_instances', []): - self._instances.remove(wr) - self.close() - except: - pass - def _feature_detect(self): """ Detect which features are available on the DAC by querying various parameters. diff --git a/qcodes/instrument_drivers/Lakeshore/Model_336.py b/qcodes/instrument_drivers/Lakeshore/Model_336.py index 2696781e3099..96f6567bb5cc 100644 --- a/qcodes/instrument_drivers/Lakeshore/Model_336.py +++ b/qcodes/instrument_drivers/Lakeshore/Model_336.py @@ -1,7 +1,5 @@ - -import numpy as numpy -from qcodes import Parameter, VisaInstrument, InstrumentChannel, ChannelList -from qcodes.utils.validators import Numbers, Enum, Strings +from qcodes import VisaInstrument, InstrumentChannel, ChannelList +from qcodes.utils.validators import Enum, Strings class SensorChannel(InstrumentChannel): """ @@ -29,7 +27,7 @@ def __init__(self, parent, name, channel): self.add_parameter('sensor_status', get_cmd='RDGST? {}'.format(self._channel), val_mapping={'OK': 0, 'Invalid Reading': 1, 'Temp Underrange': 16, 'Temp Overrange': 32, 'Sensor Units Zero': 64, 'Sensor Units Overrange': 128}, label='Sensor_Status') - + self.add_parameter('sensor_name', get_cmd='INNAME? {}'.format(self._channel), get_parser=str, set_cmd='INNAME {},\"{{}}\"'.format(self._channel), vals=Strings(15), label='Sensor_Name') From 5e1d23e5be651e8bc98a34b38c7e4bd30dad8116 Mon Sep 17 00:00:00 2001 From: Sebastian Pauka Date: Tue, 30 May 2017 16:26:56 +1000 Subject: [PATCH 08/49] fix: flush is now handled correctly in instrument base class --- qcodes/instrument_drivers/Harvard/Decadac.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/qcodes/instrument_drivers/Harvard/Decadac.py b/qcodes/instrument_drivers/Harvard/Decadac.py index 72732902fdf6..97351de72add 100644 --- a/qcodes/instrument_drivers/Harvard/Decadac.py +++ b/qcodes/instrument_drivers/Harvard/Decadac.py @@ -383,9 +383,6 @@ def __init__(self, name, address, min_val=-5, max_val=5, **kwargs): super().__init__(name, address, **kwargs) - # Flush the input and output buffers, in case there is any remaining junk - self.visa_handle.flush(vi_const.VI_READ_BUF_DISCARD | vi_const.VI_WRITE_BUF_DISCARD) - # Do feature detection self._feature_detect() From 4ed3f4e81a88f36f569c58fc0239614362762b48 Mon Sep 17 00:00:00 2001 From: Sebastian Pauka Date: Tue, 30 May 2017 17:40:38 +1000 Subject: [PATCH 09/49] feat: Allow channellists to be excluded from snapshots Allows certain channel lists to be excluded from snapshots. This is helpful when, for example, there are multiple ways to access the same channel. --- qcodes/instrument/channel.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index d5481d908523..687d13edc7c0 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -97,13 +97,17 @@ class ChannelList(Metadatable): chan_list (Iterable[chan_type]): An optional iterable of channels of type chan_type. This will create a list and immediately lock the ChannelList. + snapshotable (bool): Optionally disables taking of snapshots for a given channel list. + This is used when objects stored inside a channel list are accessible in multiple ways + and should not be repeated in an instrument snapshot. + Attributes: parameters (Dict[Parameter]): All the parameters supported by this group of channels. functions (Dict[Function]): All the functions supported by this group of channels """ - def __init__(self, parent, name, chan_type, chan_list=None): + def __init__(self, parent, name, chan_type, chan_list=None, snapshotable=True): super().__init__() self._parent = parent @@ -112,6 +116,7 @@ def __init__(self, parent, name, chan_type, chan_list=None): print(chan_type, InstrumentChannel) raise ValueError("Channel Lists can only hold instances of type InstrumentChannel") self._chan_type = chan_type + self._snapshotable = snapshotable # If a list of channels is not provided, define a list to store channels. # This will eventually become a locked tuple. @@ -227,21 +232,30 @@ def lock(self): self._channels = tuple(self._channels) self._locked = True - def snapshot_base(self, update=False): + def snapshot_base(self, update=False, force=False): """ State of the instrument as a JSON-compatible dict. Args: update (bool): If True, update the state by querying the - instrument. If False, just use the latest values in memory. + instrument. If False, just use the latest values in memory.. + + force (bool): When true, force a snapshot to be taken even if + the channel list is marked as not snapshotable. Returns: dict: base snapshot """ - snap = {'channels': dict((chan.name, chan.snapshot(update=update)) - for chan in self._channels), - '__class__': full_class(self), - } + if self._snapshotable or force: + snap = {'channels': dict((chan.name, chan.snapshot(update=update)) + for chan in self._channels), + 'snapshotable': self._snapshotable, + '__class__': full_class(self), + } + else: + snap = {'snapshotable': self._snapshotable, + '__class__': full_class(self), + } return snap def __getattr__(self, name): From 3a728d42c74a13a7e12369bdeb5ca55f6df3eb00 Mon Sep 17 00:00:00 2001 From: Sebastian Pauka Date: Tue, 30 May 2017 17:47:50 +1000 Subject: [PATCH 10/49] fix: Allow snapshots of ChannelLists This feature is implemented by creating the idea of a submodule in the instrument class. This is just a class which implements the Instrument class, and may be a channel list or may just implement a logical collection of parameters within a parent instrument. For example, looking at the Lakeshore Model_336 driver, each channel can either be accessed as a member of a channel list, or by using the name of the channel: ```python3 therm = Model_336("therm", "...") # These two accesses are equivalent therm.A.temperature() therm.channels[0].temperature() ``` --- qcodes/instrument/base.py | 39 ++++++++++++++++++- qcodes/instrument_drivers/Harvard/Decadac.py | 14 ++++--- .../instrument_drivers/Lakeshore/Model_336.py | 15 +++++-- 3 files changed, 57 insertions(+), 11 deletions(-) diff --git a/qcodes/instrument/base.py b/qcodes/instrument/base.py index 6c02d7c27388..a868780c5ad5 100644 --- a/qcodes/instrument/base.py +++ b/qcodes/instrument/base.py @@ -34,6 +34,10 @@ class Instrument(Metadatable, DelegateAttributes): functions (Dict[Function]): All the functions supported by this instrument. Usually populated via ``add_function`` + + submodules (Dict[Instrument]): All the submodules of this instrument + such as channel lists or logical groupings of parameters. + Usually populated via ``add_submodule`` """ shared_kwargs = () @@ -48,6 +52,7 @@ def __init__(self, name, **kwargs): super().__init__(**kwargs) self.parameters = {} self.functions = {} + self.submodules = {} self.name = str(name) @@ -306,6 +311,36 @@ def add_function(self, name, **kwargs): func = Function(name=name, instrument=self, **kwargs) self.functions[name] = func + def add_submodule(self, name, submodule): + """ + Bind one submodule to this instrument. + + Instrument subclasses can call this repeatedly in their ``__init__`` + method for every submodule of the instrument. + + Submodules can effectively be considered as instruments within the main + instrument, and should at minimum be snapshottable. For example, they can + be used to either store logical groupings of parameters, which may or may + not be repeated, or channel lists. + + Args: + name (str): how the submodule will be stored within ``instrument.submodules`` + and also how it can be addressed. + + submodule (Instrument): The submodule to be stored. + + Raises: + KeyError: if this instrument already contains a submodule with this + name. + TypeError: if the submodule that we are trying to add is not an instance + of an instrument. + """ + if name in self.submodules: + raise KeyError('Duplicate submodule name {}'.format(name)) + if not isinstance(submodule, Instrument): + raise TypeError('Submodules must be subclasses of instrument.') + self.submodules[name] = submodule + def snapshot_base(self, update=False): """ State of the instrument as a JSON-compatible dict. @@ -321,6 +356,8 @@ def snapshot_base(self, update=False): for name, param in self.parameters.items()), 'functions': dict((name, func.snapshot(update=update)) for name, func in self.functions.items()), + 'submodules': dict((name, subm.snapshot(update=update)) + for name, subm in self.submodules.items()), '__class__': full_class(self), } for attr in set(self._meta_attrs): @@ -459,7 +496,7 @@ def ask_raw(self, cmd): # etc... # # - delegate_attr_dicts = ['parameters', 'functions'] + delegate_attr_dicts = ['parameters', 'functions', 'submodules'] def __getitem__(self, key): """Delegate instrument['name'] to parameter or function 'name'.""" diff --git a/qcodes/instrument_drivers/Harvard/Decadac.py b/qcodes/instrument_drivers/Harvard/Decadac.py index 97351de72add..4871dd2ba3f3 100644 --- a/qcodes/instrument_drivers/Harvard/Decadac.py +++ b/qcodes/instrument_drivers/Harvard/Decadac.py @@ -387,13 +387,15 @@ def __init__(self, name, address, min_val=-5, max_val=5, **kwargs): self._feature_detect() # Create channels - self.channels = ChannelList(self, "Channels", DacChannel) - self.slots = ChannelList(self, "Slots", DacSlot) + channels = ChannelList(self, "Channels", DacChannel, snapshotable=False) + slots = ChannelList(self, "Slots", DacSlot) for i in range(6): # Create the 6 DAC slots - self.slots.append(DacSlot(self, "Slot{}".format(i), i, min_val, max_val)) - self.channels.extend(self.slots[i].channels) - self.slots.lock() - self.channels.lock() + slots.append(DacSlot(self, "Slot{}".format(i), i, min_val, max_val)) + channels.extend(self.slots[i].channels) + slots.lock() + channels.lock() + self.add_submodule("slots", slots) + self.add_submodule("channels", channels) # Custom connect message, since we can't identify t = time() - (self._t0) diff --git a/qcodes/instrument_drivers/Lakeshore/Model_336.py b/qcodes/instrument_drivers/Lakeshore/Model_336.py index 96f6567bb5cc..cb8779929fc8 100644 --- a/qcodes/instrument_drivers/Lakeshore/Model_336.py +++ b/qcodes/instrument_drivers/Lakeshore/Model_336.py @@ -42,9 +42,16 @@ class Model_336(VisaInstrument): def __init__(self, name, address, **kwargs): super().__init__(name, address, terminator="\r\n", **kwargs) - self.channels = ChannelList(self, "TempSensors", SensorChannel) - for chan in ('A', 'B', 'C', 'D'): - self.channels.append(SensorChannel(self, 'Chan{}'.format(chan), chan)) - self.channels.lock() + # Allow access to channels either by referring to the channel name + # or through a channel list. + # i.e. Model_336.A.temperature() and Model_336.channels[0].temperature() + # refer to the same parameter. + channels = ChannelList(self, "TempSensors", SensorChannel, snapshotable=False) + for chan_name in ('A', 'B', 'C', 'D'): + channel = SensorChannel(self, 'Chan{}'.format(chan_name), chan_name) + channels.append(channel) + self.add_submodule(chan_name, channel) + channels.lock() + self.add_submodule("channels", channels) self.connect_message() \ No newline at end of file From e5cda8b512d565865d1d7708c15a7bf0c2cd96ce Mon Sep 17 00:00:00 2001 From: Sebastian Pauka Date: Tue, 30 May 2017 18:03:02 +1000 Subject: [PATCH 11/49] style: remove force arg in snapshot_base Should think about the best way to implement this change. --- qcodes/instrument/channel.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index 687d13edc7c0..7815600ae413 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -232,7 +232,7 @@ def lock(self): self._channels = tuple(self._channels) self._locked = True - def snapshot_base(self, update=False, force=False): + def snapshot_base(self, update=False): """ State of the instrument as a JSON-compatible dict. @@ -240,13 +240,10 @@ def snapshot_base(self, update=False, force=False): update (bool): If True, update the state by querying the instrument. If False, just use the latest values in memory.. - force (bool): When true, force a snapshot to be taken even if - the channel list is marked as not snapshotable. - Returns: dict: base snapshot """ - if self._snapshotable or force: + if self._snapshotable: snap = {'channels': dict((chan.name, chan.snapshot(update=update)) for chan in self._channels), 'snapshotable': self._snapshotable, From f4172d23e6c42c7b7ce1686661aeb44d53f4c25e Mon Sep 17 00:00:00 2001 From: Sebastian Pauka Date: Tue, 30 May 2017 18:03:12 +1000 Subject: [PATCH 12/49] style: remove unessecary import --- qcodes/instrument_drivers/Harvard/Decadac.py | 1 - 1 file changed, 1 deletion(-) diff --git a/qcodes/instrument_drivers/Harvard/Decadac.py b/qcodes/instrument_drivers/Harvard/Decadac.py index 4871dd2ba3f3..487f64de2e92 100644 --- a/qcodes/instrument_drivers/Harvard/Decadac.py +++ b/qcodes/instrument_drivers/Harvard/Decadac.py @@ -2,7 +2,6 @@ from functools import partial from qcodes import VisaInstrument, InstrumentChannel, ChannelList, ManualParameter from qcodes.utils import validators as vals -from visa import constants as vi_const class DACException(Exception): pass From 3aabf2722e40d354d5e8420a19410e2387e9fa93 Mon Sep 17 00:00:00 2001 From: Sebastian Pauka Date: Tue, 30 May 2017 18:05:49 +1000 Subject: [PATCH 13/49] fix: error in type check. --- qcodes/instrument/channel.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index 7815600ae413..bc140ed2bd47 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -112,8 +112,7 @@ def __init__(self, parent, name, chan_type, chan_list=None, snapshotable=True): self._parent = parent self._name = name - if not isinstance(chan_type, type) != type or not issubclass(chan_type, InstrumentChannel): - print(chan_type, InstrumentChannel) + if not isinstance(chan_type, type) or not issubclass(chan_type, InstrumentChannel): raise ValueError("Channel Lists can only hold instances of type InstrumentChannel") self._chan_type = chan_type self._snapshotable = snapshotable From 4a3e517c41fbca7e73897b400d248697a099cbb1 Mon Sep 17 00:00:00 2001 From: Sebastian Pauka Date: Tue, 30 May 2017 22:33:11 +1000 Subject: [PATCH 14/49] fix: submodules should be Metadatable Submodule objects must be Metadatables at minimum, instrument is too restrictive for channels. --- qcodes/instrument/base.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/qcodes/instrument/base.py b/qcodes/instrument/base.py index a868780c5ad5..2151d9cb2b77 100644 --- a/qcodes/instrument/base.py +++ b/qcodes/instrument/base.py @@ -35,7 +35,7 @@ class Instrument(Metadatable, DelegateAttributes): functions (Dict[Function]): All the functions supported by this instrument. Usually populated via ``add_function`` - submodules (Dict[Instrument]): All the submodules of this instrument + submodules (Dict[Metadatable]): All the submodules of this instrument such as channel lists or logical groupings of parameters. Usually populated via ``add_submodule`` """ @@ -327,18 +327,18 @@ def add_submodule(self, name, submodule): name (str): how the submodule will be stored within ``instrument.submodules`` and also how it can be addressed. - submodule (Instrument): The submodule to be stored. + submodule (Metadatable): The submodule to be stored. Raises: KeyError: if this instrument already contains a submodule with this name. TypeError: if the submodule that we are trying to add is not an instance - of an instrument. + of an Metadatable object. """ if name in self.submodules: raise KeyError('Duplicate submodule name {}'.format(name)) - if not isinstance(submodule, Instrument): - raise TypeError('Submodules must be subclasses of instrument.') + if not isinstance(submodule, Metadatable): + raise TypeError('Submodules must be metadatable.') self.submodules[name] = submodule def snapshot_base(self, update=False): From 703cc8425426a3fd8dcfe0f0d2453710adb44dd2 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Tue, 30 May 2017 17:15:44 +0200 Subject: [PATCH 15/49] Start adding tests for channels --- qcodes/tests/instrument_mocks.py | 34 +++++++++++++++++++++++++- qcodes/tests/test_channels.py | 42 ++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 qcodes/tests/test_channels.py diff --git a/qcodes/tests/instrument_mocks.py b/qcodes/tests/instrument_mocks.py index 4c757a830e63..b2ffc532d2c9 100644 --- a/qcodes/tests/instrument_mocks.py +++ b/qcodes/tests/instrument_mocks.py @@ -3,7 +3,7 @@ from qcodes.instrument.base import Instrument from qcodes.utils.validators import Numbers from qcodes.instrument.parameter import MultiParameter, ManualParameter - +from qcodes.instrument.channel import InstrumentChannel, ChannelList class MockParabola(Instrument): ''' @@ -107,6 +107,38 @@ def __init__(self, name='dummy', gates=['dac1', 'dac2', 'dac3'], **kwargs): unit="V", vals=Numbers(-800, 400)) +class DummyChannel(InstrumentChannel): + """ + A single dummy channel implementation + """ + + def __init__(self, parent, name, channel): + super().__init__(parent, name) + + self._channel = channel + + # Add the various channel parameters + self.add_parameter('temperature', + parameter_class=ManualParameter, + initial_value=0, + label="Temperature_{}".format(channel), + unit='K', + vals=Numbers(0, 300)) + +class DummyChannelInstrument(Instrument): + """ + Dummy instrument with channels + """ + + def __init__(self, name, **kwargs): + super().__init__(name, **kwargs) + + channels = ChannelList(self, "TempSensors", DummyChannel, snapshotable=False) + for chan_name in ('A', 'B', 'C', 'D'): + channel = DummyChannel(self, 'Chan{}'.format(chan_name), chan_name) + channels.append(channel) + self.add_submodule(chan_name, channel) + self.add_submodule("channels", channels) class MultiGetter(MultiParameter): """ diff --git a/qcodes/tests/test_channels.py b/qcodes/tests/test_channels.py new file mode 100644 index 000000000000..73f72bd03b02 --- /dev/null +++ b/qcodes/tests/test_channels.py @@ -0,0 +1,42 @@ +from unittest import TestCase +import gc + +from .instrument_mocks import DummyChannelInstrument, DummyChannel + +from hypothesis import given +from hypothesis.strategies import floats, integers + + +class TestChannels(TestCase): + + def setUp(self): + self.instrument = DummyChannelInstrument(name='testchanneldummy') + + def tearDown(self): + # force gc run + del self.instrument + gc.collect() + + def test_channels_get(self): + + temperatures = self.instrument.channels.temperature.get() + self.assertEqual(len(temperatures), 4) + + @given(value=floats(0, 300), channel=integers(0, 3)) + def test_channel_access_is_identical(self, value, channel): + channel_to_label = {0: 'A', 1: 'B', 2: 'C', 3: "D"} + label = channel_to_label[channel] + channel_via_label = getattr(self.instrument, label) + channel_via_label.temperature(value) + self.assertEqual(channel_via_label.temperature(), value) + self.assertEqual(self.instrument.channels[channel].temperature(), value) + self.assertEqual(self.instrument.channels.temperature()[channel], value) + + def test_add_channel(self): + name = 'foo' + channel = DummyChannel(self.instrument, 'Chan'+name, name) + self.instrument.channels.append(channel) + self.instrument.add_submodule(name, channel) + + self.assertEqual(len(self.instrument.channels), 5) + From 1066eedcd45deb594684e3e00eca708d6be9cabf Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Tue, 30 May 2017 17:24:25 +0200 Subject: [PATCH 16/49] Extend tests --- qcodes/tests/test_channels.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/qcodes/tests/test_channels.py b/qcodes/tests/test_channels.py index 73f72bd03b02..59c1b23745c7 100644 --- a/qcodes/tests/test_channels.py +++ b/qcodes/tests/test_channels.py @@ -27,10 +27,24 @@ def test_channel_access_is_identical(self, value, channel): channel_to_label = {0: 'A', 1: 'B', 2: 'C', 3: "D"} label = channel_to_label[channel] channel_via_label = getattr(self.instrument, label) + # set via labeled channel channel_via_label.temperature(value) self.assertEqual(channel_via_label.temperature(), value) self.assertEqual(self.instrument.channels[channel].temperature(), value) self.assertEqual(self.instrument.channels.temperature()[channel], value) + # reset + channel_via_label.temperature(0) + self.assertEqual(channel_via_label.temperature(), 0) + self.assertEqual(self.instrument.channels[channel].temperature(), 0) + self.assertEqual(self.instrument.channels.temperature()[channel], 0) + # set via index into list + self.instrument.channels[channel].temperature(value) + self.assertEqual(channel_via_label.temperature(), value) + self.assertEqual(self.instrument.channels[channel].temperature(), value) + self.assertEqual(self.instrument.channels.temperature()[channel], value) + # it's not possible to set via self.instrument.channels.temperature as this is a multi parameter + # that currently does not support set. + def test_add_channel(self): name = 'foo' @@ -39,4 +53,4 @@ def test_add_channel(self): self.instrument.add_submodule(name, channel) self.assertEqual(len(self.instrument.channels), 5) - + From 8d5dbc8d75c892822efb915a7f333a5034b2b5ef Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Thu, 1 Jun 2017 17:38:03 +0200 Subject: [PATCH 17/49] Fix: ensure consistent dataset names regardless of chan[] or chanA reading --- qcodes/instrument/channel.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index bc140ed2bd47..ba00c95c6275 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -266,7 +266,7 @@ def __getattr__(self, name): if name in self._channels[0].parameters: # We need to construct a MultiParameter object to get each of the # values our of each parameter in our list - names = tuple("{}.{}".format(chan.name, name) for chan in self._channels) + names = tuple("{}_{}".format(chan.name, name) for chan in self._channels) shapes = tuple(() for chan in self._channels) #TODO: Pull shapes intelligently labels = tuple(chan.parameters[name].label for chan in self._channels) units = tuple(chan.parameters[name].unit for chan in self._channels) @@ -308,4 +308,13 @@ def get(self): """ Return a tuple containing the data from each of the channels in the list """ - return tuple(chan.parameters[self._param_name].get() for chan in self._channels) \ No newline at end of file + return tuple(chan.parameters[self._param_name].get() for chan in self._channels) + + @property + def full_names(self): + """Overwrite full_names because the instument name is already included in the name. + This happens because the instument name is included in the channel name merged into the + parameter name above. + """ + + return self.names \ No newline at end of file From f9e3b1fae54a62dc926cf664f7a8786d2067a41f Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Thu, 1 Jun 2017 17:38:22 +0200 Subject: [PATCH 18/49] More work on channels testing --- qcodes/tests/test_channels.py | 99 +++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 5 deletions(-) diff --git a/qcodes/tests/test_channels.py b/qcodes/tests/test_channels.py index 59c1b23745c7..a6d483a5abda 100644 --- a/qcodes/tests/test_channels.py +++ b/qcodes/tests/test_channels.py @@ -1,21 +1,32 @@ from unittest import TestCase -import gc +import unittest -from .instrument_mocks import DummyChannelInstrument, DummyChannel +from qcodes.tests.instrument_mocks import DummyChannelInstrument, DummyChannel +from qcodes.utils.validators import Numbers +from qcodes.instrument.parameter import ManualParameter -from hypothesis import given +from hypothesis import given, settings +import hypothesis.strategies as hst from hypothesis.strategies import floats, integers +from qcodes.loops import Loop +from numpy.testing import assert_array_equal + class TestChannels(TestCase): def setUp(self): + # print("setup") self.instrument = DummyChannelInstrument(name='testchanneldummy') def tearDown(self): - # force gc run + + self.instrument.close() del self.instrument - gc.collect() + # del self.instrument is not sufficient in general because the __del__ method is + # first invoked when there are 0 (non weak) references to the instrument. If a test + # fails the unittest framework will keep a reference to the instrument is removed from + # the testcase and __del__ is not invoked until all the tests have run. def test_channels_get(self): @@ -54,3 +65,81 @@ def test_add_channel(self): self.assertEqual(len(self.instrument.channels), 5) + self.instrument.channels.lock() + # after locking the channels it's not possible to add any more channels + with self.assertRaises(AttributeError): + name = 'bar' + channel = DummyChannel(self.instrument, 'Chan' + name, name) + self.instrument.channels.append(channel) + self.instrument.add_submodule(name, channel) + + + +class TestChannelsLoop(TestCase): + pass + + def setUp(self): + self.instrument = DummyChannelInstrument(name='testchanneldummy') + + def tearDown(self): + self.instrument.close() + del self.instrument + + def test_loop_simple(self): + loop = Loop(self.instrument.channels[0].temperature.sweep(0,300, 10), 0.001).each(self.instrument.A.temperature) + data = loop.run() + assert_array_equal(data.testchanneldummy_ChanA_temperature_set.ndarray, + data.testchanneldummy_ChanA_temperature.ndarray) + + def test_loop_measure_all_channels(self): + p1 = ManualParameter(name='p1', vals=Numbers(-10, 10)) + loop = Loop(p1.sweep(-10,10,1), 1e-6).each(self.instrument.channels.temperature) + data = loop.run() + self.assertEqual(data.p1_set.ndarray.shape, (21, )) + for i,chan in enumerate(['A', 'B', 'C', 'D']): + self.assertEqual(getattr(data, 'testchanneldummy_Chan{}_temperature'.format(chan)).ndarray.shape, (21,)) + + + def test_loop_measure_channels_individually(self): + p1 = ManualParameter(name='p1', vals=Numbers(-10, 10)) + loop = Loop(p1.sweep(-10,10,1), 1e-6).each(self.instrument.channels[0].temperature, + self.instrument.channels[1].temperature, + self.instrument.channels[2].temperature, + self.instrument.channels[3].temperature) + data = loop.run() + self.assertEqual(data.p1_set.ndarray.shape, (21, )) + for i,chan in enumerate(['A', 'B', 'C', 'D']): + self.assertEqual(getattr(data, 'testchanneldummy_Chan{}_temperature'.format(chan)).ndarray.shape, (21,)) + + @given(values=hst.tuples(floats(0, 300), floats(0, 300), floats(0, 300), floats(0, 300))) + @settings(max_examples=10) + def test_loop_measure_channels_by_name(self, values): + p1 = ManualParameter(name='p1', vals=Numbers(-10, 10)) + for i in range(4): + self.instrument.channels[i].temperature(values[i]) + loop = Loop(p1.sweep(-10,10,1), 1e-6).each(self.instrument.A.temperature, + self.instrument.B.temperature, + self.instrument.C.temperature, + self.instrument.D.temperature) + data = loop.run() + self.assertEqual(data.p1_set.ndarray.shape, (21, )) + for i,chan in enumerate(['A', 'B', 'C', 'D']): + self.assertEqual(getattr(data, 'testchanneldummy_Chan{}_temperature'.format(chan)).ndarray.shape, (21,)) + self.assertEqual(getattr(data, 'testchanneldummy_Chan{}_temperature'.format(chan)).ndarray.max(), values[i]) + # + # @given(loop_channel=hst.lists(integers(0, 3), measure_channel=integers(0, 3)) + # @settings(max_examples=10) + def test_nested_loop_over_channels(self, loop_channels=(0,1), measure_channel=2): + channel_to_label = {0: 'A', 1: 'B', 2: 'C', 3: "D"} + loop = Loop(self.instrument.channels[loop_channels[0]].temperature.sweep(0,10,1)) + loop = loop.loop(self.instrument.channels[loop_channels[1]].temperature.sweep(50,51,0.1)) + loop = loop.each(self.instrument.channels[measure_channel].temperature) + data = loop.run() + # for i,chan in enumerate(['A', 'B', 'C', 'D']): + # self.assertEqual(getattr(data, 'testchanneldummy_Chan{}_temperature'.format(chan)).ndarray.shape, (21,)) + # self.assertEqual(getattr(data, 'testchanneldummy_Chan{}_temperature'.format(chan)).ndarray.max(), values[i]) + + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file From be7e8ae75f5ceb7ae689d4408bb5a1e0cb66c494 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Thu, 1 Jun 2017 17:38:37 +0200 Subject: [PATCH 19/49] Make sure that instument is closed in tests --- qcodes/tests/test_instrument.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/qcodes/tests/test_instrument.py b/qcodes/tests/test_instrument.py index 55f8f8e8849a..555a896e85f3 100644 --- a/qcodes/tests/test_instrument.py +++ b/qcodes/tests/test_instrument.py @@ -17,6 +17,8 @@ def setUp(self): def tearDown(self): # force gc run + self.instrument.close() + self.instrument2.close() del self.instrument del self.instrument2 gc.collect() @@ -37,6 +39,7 @@ def test_check_instances(self): self.assertEqual(DummyInstrument.instances(), [self.instrument]) self.assertEqual(self.instrument.instances(), [self.instrument]) + def test_attr_access(self): instrument = self.instrument From 4a8d0ae4c6db9ebcd6f98fce49805909374847f7 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Thu, 1 Jun 2017 17:39:01 +0200 Subject: [PATCH 20/49] Depend on hypothesis for test --- test_requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test_requirements.txt b/test_requirements.txt index ff0ae9589438..6e769b3eccf1 100644 --- a/test_requirements.txt +++ b/test_requirements.txt @@ -1,4 +1,5 @@ coverage pytest-cov pytest -codacy-coverage \ No newline at end of file +codacy-coverage +hypothesis \ No newline at end of file From 8e92b274c7df00a4b3d4fb4b6e78c2e037906239 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Fri, 2 Jun 2017 13:04:38 +0200 Subject: [PATCH 21/49] fix improve testing of channels --- qcodes/tests/test_channels.py | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/qcodes/tests/test_channels.py b/qcodes/tests/test_channels.py index a6d483a5abda..5b62f0ae11ec 100644 --- a/qcodes/tests/test_channels.py +++ b/qcodes/tests/test_channels.py @@ -10,8 +10,8 @@ from hypothesis.strategies import floats, integers from qcodes.loops import Loop - -from numpy.testing import assert_array_equal +import numpy as np +from numpy.testing import assert_array_equal, assert_allclose class TestChannels(TestCase): @@ -126,20 +126,30 @@ def test_loop_measure_channels_by_name(self, values): for i,chan in enumerate(['A', 'B', 'C', 'D']): self.assertEqual(getattr(data, 'testchanneldummy_Chan{}_temperature'.format(chan)).ndarray.shape, (21,)) self.assertEqual(getattr(data, 'testchanneldummy_Chan{}_temperature'.format(chan)).ndarray.max(), values[i]) - # - # @given(loop_channel=hst.lists(integers(0, 3), measure_channel=integers(0, 3)) - # @settings(max_examples=10) - def test_nested_loop_over_channels(self, loop_channels=(0,1), measure_channel=2): + + @given(loop_channels=hst.lists(integers(0, 3), min_size=2, max_size=2, unique=True), measure_channel=integers(0, 3)) + @settings(max_examples=10) + def test_nested_loop_over_channels(self, loop_channels, measure_channel): channel_to_label = {0: 'A', 1: 'B', 2: 'C', 3: "D"} - loop = Loop(self.instrument.channels[loop_channels[0]].temperature.sweep(0,10,1)) - loop = loop.loop(self.instrument.channels[loop_channels[1]].temperature.sweep(50,51,0.1)) + loop = Loop(self.instrument.channels[loop_channels[0]].temperature.sweep(0, 10, 0.5)) + loop = loop.loop(self.instrument.channels[loop_channels[1]].temperature.sweep(50, 51, 0.1)) loop = loop.each(self.instrument.channels[measure_channel].temperature) data = loop.run() - # for i,chan in enumerate(['A', 'B', 'C', 'D']): - # self.assertEqual(getattr(data, 'testchanneldummy_Chan{}_temperature'.format(chan)).ndarray.shape, (21,)) - # self.assertEqual(getattr(data, 'testchanneldummy_Chan{}_temperature'.format(chan)).ndarray.max(), values[i]) + self.assertEqual(getattr(data, 'testchanneldummy_Chan{}_temperature_set'.format( + channel_to_label[loop_channels[0]])).ndarray.shape, (21,)) + self.assertEqual(getattr(data, 'testchanneldummy_Chan{}_temperature_set'.format( + channel_to_label[loop_channels[1]])).ndarray.shape, (21,11,)) + self.assertEqual(getattr(data, 'testchanneldummy_Chan{}_temperature'.format( + channel_to_label[measure_channel])).ndarray.shape, (21,11)) + + assert_array_equal(getattr(data, 'testchanneldummy_Chan{}_temperature_set'.format( + channel_to_label[loop_channels[0]])).ndarray, + np.arange(0,10.1,0.5)) + expected_array = np.repeat(np.arange(50, 51.01, 0.1).reshape(1, 11), 21, axis=0) + array = getattr(data, 'testchanneldummy_Chan{}_temperature_set'.format(channel_to_label[loop_channels[1]])).ndarray + assert_allclose(array, expected_array) if __name__ == '__main__': unittest.main() \ No newline at end of file From cc6622def2d1a135799da4c0d36fcfad0971f19e Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Fri, 2 Jun 2017 17:35:26 +0200 Subject: [PATCH 22/49] test more: --- qcodes/tests/instrument_mocks.py | 2 +- qcodes/tests/test_channels.py | 72 ++++++++++++++++++++------------ 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/qcodes/tests/instrument_mocks.py b/qcodes/tests/instrument_mocks.py index b2ffc532d2c9..bfdb1e2afdb8 100644 --- a/qcodes/tests/instrument_mocks.py +++ b/qcodes/tests/instrument_mocks.py @@ -134,7 +134,7 @@ def __init__(self, name, **kwargs): super().__init__(name, **kwargs) channels = ChannelList(self, "TempSensors", DummyChannel, snapshotable=False) - for chan_name in ('A', 'B', 'C', 'D'): + for chan_name in ('A', 'B', 'C', 'D', 'E', 'F'): channel = DummyChannel(self, 'Chan{}'.format(chan_name), chan_name) channels.append(channel) self.add_submodule(chan_name, channel) diff --git a/qcodes/tests/test_channels.py b/qcodes/tests/test_channels.py index 5b62f0ae11ec..12c802bdf8ac 100644 --- a/qcodes/tests/test_channels.py +++ b/qcodes/tests/test_channels.py @@ -7,12 +7,12 @@ from hypothesis import given, settings import hypothesis.strategies as hst -from hypothesis.strategies import floats, integers from qcodes.loops import Loop import numpy as np from numpy.testing import assert_array_equal, assert_allclose + class TestChannels(TestCase): def setUp(self): @@ -31,9 +31,9 @@ def tearDown(self): def test_channels_get(self): temperatures = self.instrument.channels.temperature.get() - self.assertEqual(len(temperatures), 4) + self.assertEqual(len(temperatures), 6) - @given(value=floats(0, 300), channel=integers(0, 3)) + @given(value=hst.floats(0, 300), channel=hst.integers(0, 3)) def test_channel_access_is_identical(self, value, channel): channel_to_label = {0: 'A', 1: 'B', 2: 'C', 3: "D"} label = channel_to_label[channel] @@ -56,14 +56,13 @@ def test_channel_access_is_identical(self, value, channel): # it's not possible to set via self.instrument.channels.temperature as this is a multi parameter # that currently does not support set. - def test_add_channel(self): name = 'foo' channel = DummyChannel(self.instrument, 'Chan'+name, name) self.instrument.channels.append(channel) self.instrument.add_submodule(name, channel) - self.assertEqual(len(self.instrument.channels), 5) + self.assertEqual(len(self.instrument.channels), 7) self.instrument.channels.lock() # after locking the channels it's not possible to add any more channels @@ -73,6 +72,23 @@ def test_add_channel(self): self.instrument.channels.append(channel) self.instrument.add_submodule(name, channel) + @given(setpoints=hst.lists(hst.floats(0, 300), min_size=4, max_size=4)) + def test_combine_channels(self, setpoints): + self.assertEqual(len(self.instrument.channels), 6) + + mychannels = self.instrument.channels[0:2] + self.instrument.channels[4:] + + self.assertEqual(len(mychannels), 4) + assert mychannels[0] is self.instrument.A + assert mychannels[1] is self.instrument.B + assert mychannels[2] is self.instrument.E + assert mychannels[3] is self.instrument.F + + for i in range(len(mychannels)): + mychannels[i].temperature(setpoints[i]) + + expected = tuple(setpoints[0:2] + [0, 0] + setpoints[2:]) + self.assertEquals(self.instrument.channels.temperature(), expected) class TestChannelsLoop(TestCase): @@ -86,48 +102,51 @@ def tearDown(self): del self.instrument def test_loop_simple(self): - loop = Loop(self.instrument.channels[0].temperature.sweep(0,300, 10), 0.001).each(self.instrument.A.temperature) + loop = Loop(self.instrument.channels[0].temperature.sweep(0, 300, 10), + 0.001).each(self.instrument.A.temperature) data = loop.run() assert_array_equal(data.testchanneldummy_ChanA_temperature_set.ndarray, data.testchanneldummy_ChanA_temperature.ndarray) def test_loop_measure_all_channels(self): p1 = ManualParameter(name='p1', vals=Numbers(-10, 10)) - loop = Loop(p1.sweep(-10,10,1), 1e-6).each(self.instrument.channels.temperature) + loop = Loop(p1.sweep(-10, 10, 1), 1e-6).each(self.instrument.channels.temperature) data = loop.run() self.assertEqual(data.p1_set.ndarray.shape, (21, )) - for i,chan in enumerate(['A', 'B', 'C', 'D']): + self.assertEqual(len(data.arrays), 7) + for i, chan in enumerate(['A', 'B', 'C', 'D', 'E', 'F']): self.assertEqual(getattr(data, 'testchanneldummy_Chan{}_temperature'.format(chan)).ndarray.shape, (21,)) - def test_loop_measure_channels_individually(self): p1 = ManualParameter(name='p1', vals=Numbers(-10, 10)) - loop = Loop(p1.sweep(-10,10,1), 1e-6).each(self.instrument.channels[0].temperature, - self.instrument.channels[1].temperature, - self.instrument.channels[2].temperature, - self.instrument.channels[3].temperature) + loop = Loop(p1.sweep(-10, 10, 1), 1e-6).each(self.instrument.channels[0].temperature, + self.instrument.channels[1].temperature, + self.instrument.channels[2].temperature, + self.instrument.channels[3].temperature) data = loop.run() self.assertEqual(data.p1_set.ndarray.shape, (21, )) - for i,chan in enumerate(['A', 'B', 'C', 'D']): + for i, chan in enumerate(['A', 'B', 'C', 'D']): self.assertEqual(getattr(data, 'testchanneldummy_Chan{}_temperature'.format(chan)).ndarray.shape, (21,)) - @given(values=hst.tuples(floats(0, 300), floats(0, 300), floats(0, 300), floats(0, 300))) + @given(values=hst.lists(hst.floats(0, 300), min_size=4, max_size=4)) @settings(max_examples=10) def test_loop_measure_channels_by_name(self, values): p1 = ManualParameter(name='p1', vals=Numbers(-10, 10)) for i in range(4): self.instrument.channels[i].temperature(values[i]) - loop = Loop(p1.sweep(-10,10,1), 1e-6).each(self.instrument.A.temperature, - self.instrument.B.temperature, - self.instrument.C.temperature, - self.instrument.D.temperature) + loop = Loop(p1.sweep(-10, 10, 1), 1e-6).each(self.instrument.A.temperature, + self.instrument.B.temperature, + self.instrument.C.temperature, + self.instrument.D.temperature) data = loop.run() self.assertEqual(data.p1_set.ndarray.shape, (21, )) - for i,chan in enumerate(['A', 'B', 'C', 'D']): + for i, chan in enumerate(['A', 'B', 'C', 'D']): self.assertEqual(getattr(data, 'testchanneldummy_Chan{}_temperature'.format(chan)).ndarray.shape, (21,)) self.assertEqual(getattr(data, 'testchanneldummy_Chan{}_temperature'.format(chan)).ndarray.max(), values[i]) + self.assertEqual(getattr(data, 'testchanneldummy_Chan{}_temperature'.format(chan)).ndarray.min(), values[i]) - @given(loop_channels=hst.lists(integers(0, 3), min_size=2, max_size=2, unique=True), measure_channel=integers(0, 3)) + @given(loop_channels=hst.lists(hst.integers(0, 3), min_size=2, max_size=2, unique=True), + measure_channel=hst.integers(0, 3)) @settings(max_examples=10) def test_nested_loop_over_channels(self, loop_channels, measure_channel): channel_to_label = {0: 'A', 1: 'B', 2: 'C', 3: "D"} @@ -139,17 +158,18 @@ def test_nested_loop_over_channels(self, loop_channels, measure_channel): self.assertEqual(getattr(data, 'testchanneldummy_Chan{}_temperature_set'.format( channel_to_label[loop_channels[0]])).ndarray.shape, (21,)) self.assertEqual(getattr(data, 'testchanneldummy_Chan{}_temperature_set'.format( - channel_to_label[loop_channels[1]])).ndarray.shape, (21,11,)) + channel_to_label[loop_channels[1]])).ndarray.shape, (21, 11,)) self.assertEqual(getattr(data, 'testchanneldummy_Chan{}_temperature'.format( - channel_to_label[measure_channel])).ndarray.shape, (21,11)) + channel_to_label[measure_channel])).ndarray.shape, (21, 11)) assert_array_equal(getattr(data, 'testchanneldummy_Chan{}_temperature_set'.format( channel_to_label[loop_channels[0]])).ndarray, - np.arange(0,10.1,0.5)) + np.arange(0, 10.1, 0.5)) expected_array = np.repeat(np.arange(50, 51.01, 0.1).reshape(1, 11), 21, axis=0) - array = getattr(data, 'testchanneldummy_Chan{}_temperature_set'.format(channel_to_label[loop_channels[1]])).ndarray + array = getattr(data, 'testchanneldummy_Chan' + '{}_temperature_set'.format(channel_to_label[loop_channels[1]])).ndarray assert_allclose(array, expected_array) if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() From 8530671f32bbe415cff28ec2aa9a0126c36a63b2 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Tue, 6 Jun 2017 10:59:05 +0200 Subject: [PATCH 23/49] fix: channels pep8 --- qcodes/instrument/channel.py | 52 +++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index ba00c95c6275..83d14c6ff72d 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -1,10 +1,12 @@ """ Base class for the channel of an instrument """ +from typing import List, Tuple, Union from .base import Instrument from .parameter import MultiParameter from ..utils.metadata import Metadatable from ..utils.helpers import full_class + class InstrumentChannel(Instrument): """ Base class for a channel in an instrument @@ -39,8 +41,10 @@ def __init__(self, parent, name, **kwargs): def __repr__(self): """Custom repr to give parent information""" - return '<{}: {} of {}: {}>'.format(type(self).__name__, self.name, - type(self._parent).__name__, self._parent.name) + return '<{}: {} of {}: {}>'.format(type(self).__name__, + self.name, + type(self._parent).__name__, + self._parent.name) # We aren't a member of the global list of instruments, don't try and remove ourself def __del__(self): @@ -74,14 +78,17 @@ def find_instruments(cls, name, instrument_class=None): # Pass any commands to read or write from the instrument up to the parent def write(self, cmd): return self._parent.write(cmd) + def write_raw(self, cmd): return self._parent.write_raw(cmd) def ask(self, cmd): return self._parent.ask(cmd) + def ask_raw(self, cmd): return self._parent.ask_raw(cmd) + class ChannelList(Metadatable): """ Container for channelized parameters that allows for sweeps over all channels, as well @@ -128,6 +135,8 @@ def __init__(self, parent, name, chan_type, chan_list=None, snapshotable=True): if not all(isinstance(chan, chan_type) for chan in self._channels): raise TypeError("All items in this channel list must be of type {}.".format(chan_type.__name__)) + + def __getitem__(self, i): """ Return either a single channel, or a new ChannelList containing only the specified channels @@ -162,8 +171,8 @@ def __add__(self, other): type(self).__name__, type(other).__name__)) if self._chan_type != other._chan_type: raise TypeError("Both l and r arguments to add must contain channels of the same type." - " Adding channels of type {} and {}.".format(self._chan_type.__name__, - other._chan_type.__name__)) + " Adding channels of type {} and {}.".format(self._chan_type.__name__, + other._chan_type.__name__)) if self._parent != other._parent: raise ValueError("Can only add channels from the same parent together.") @@ -174,13 +183,14 @@ def append(self, obj): When initially constructing the channel list, a new channel to add to the end of the list Args: - object(chan_type): New channel to add to the list. + obj(chan_type): New channel to add to the list. """ if self._locked: raise AttributeError("Cannot append to a locked channel list") if not isinstance(obj, self._chan_type): raise TypeError("All items in a channel list must be of the same type." - " Adding {} to a list of {}.".format(type(obj).__name__, self._chan_type.__name__)) + " Adding {} to a list of {}.".format(type(obj).__name__, + self._chan_type.__name__)) return self._channels.append(obj) def extend(self, objects): @@ -192,7 +202,7 @@ def extend(self, objects): """ if self._locked: raise AttributeError("Cannot extend a locked channel list") - if not all(isinstance(object, self._chan_type) for object in objects): + if not all(isinstance(obj, self._chan_type) for obj in objects): raise TypeError("All items in a channel list must be of the same type.") return self._channels.extend(objects) @@ -201,7 +211,7 @@ def index(self, obj): Return the index of the given object Args: - object(chan_type): The object to find in the channel list. + obj(chan_type): The object to find in the channel list. """ return self._channels.index(obj) @@ -212,13 +222,14 @@ def insert(self, index, obj): Args: index(int): Index to insert object. - object(chan_type): Object of type chan_type to insert. + obj(chan_type): Object of type chan_type to insert. """ if self._locked: raise AttributeError("Cannot insert into a locked channel list") if not isinstance(obj, self._chan_type): raise TypeError("All items in a channel list must be of the same type." - " Adding {} to a list of {}.".format(type(obj).__name__, self._chan_type.__name__)) + " Adding {} to a list of {}.".format(type(obj).__name__, + self._chan_type.__name__)) return self._channels.insert(index, obj) def lock(self): @@ -244,7 +255,7 @@ def snapshot_base(self, update=False): """ if self._snapshotable: snap = {'channels': dict((chan.name, chan.snapshot(update=update)) - for chan in self._channels), + for chan in self._channels), 'snapshotable': self._snapshotable, '__class__': full_class(self), } @@ -267,14 +278,18 @@ def __getattr__(self, name): # We need to construct a MultiParameter object to get each of the # values our of each parameter in our list names = tuple("{}_{}".format(chan.name, name) for chan in self._channels) - shapes = tuple(() for chan in self._channels) #TODO: Pull shapes intelligently + shapes = tuple(() for chan in self._channels) # TODO: Pull shapes intelligently labels = tuple(chan.parameters[name].label for chan in self._channels) units = tuple(chan.parameters[name].unit for chan in self._channels) - param = MultiChannelInstrumentParameter(self._channels, name, - name="Multi_{}".format(name), - names=names, shapes=shapes, instrument=self._parent, - labels=labels, units=units) + param = MultiChannelInstrumentParameter(self._channels, + param_name=name, + name="Multi_{}".format(name), + names=names, + shapes=shapes, + instrument=self._parent, + labels=labels, + units=units) return param # Check if this is a valid function @@ -288,6 +303,7 @@ def multi_func(*args, **kwargs): raise AttributeError('\'{}\' object has no attribute \'{}\''.format(self.__class__.__name__, name)) + class MultiChannelInstrumentParameter(MultiParameter): """ Parameter to get or set multiple channels simultaneously. @@ -299,7 +315,7 @@ class MultiChannelInstrumentParameter(MultiParameter): param_name(str): Name of the multichannel parameter """ - def __init__(self, channels, param_name, *args, **kwargs): + def __init__(self, channels: Union[List, Tuple], param_name, *args, **kwargs): super().__init__(*args, **kwargs) self._channels = channels self._param_name = param_name @@ -317,4 +333,4 @@ def full_names(self): parameter name above. """ - return self.names \ No newline at end of file + return self.names From 429cf7736141f924d7d9bd55ec51ce241b2a4c7f Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Tue, 6 Jun 2017 11:25:04 +0200 Subject: [PATCH 24/49] fix: ensure that channels can be extended from generator --- qcodes/instrument/channel.py | 3 +++ qcodes/tests/test_channels.py | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index 83d14c6ff72d..37855c3e99d9 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -200,6 +200,9 @@ def extend(self, objects): Args: objects(Iterable[chan_type]): A list of objects to add into the ChannelList. """ + # objects may be a generator but we need to iterate over it twice below so + # copy it into a tuple just in case. + objects = tuple(objects) if self._locked: raise AttributeError("Cannot extend a locked channel list") if not all(isinstance(obj, self._chan_type) for obj in objects): diff --git a/qcodes/tests/test_channels.py b/qcodes/tests/test_channels.py index 12c802bdf8ac..d7044db257ff 100644 --- a/qcodes/tests/test_channels.py +++ b/qcodes/tests/test_channels.py @@ -72,6 +72,15 @@ def test_add_channel(self): self.instrument.channels.append(channel) self.instrument.add_submodule(name, channel) + def test_add_channels_from_generator(self): + n_channels = len(self.instrument.channels) + names = ('foo', 'bar', 'foobar') + channels = (DummyChannel(self.instrument, 'Chan'+name, name) for name in names) + self.instrument.channels.extend(channels) + + self.assertEqual(len(self.instrument.channels), n_channels + len(names)) + + @given(setpoints=hst.lists(hst.floats(0, 300), min_size=4, max_size=4)) def test_combine_channels(self, setpoints): self.assertEqual(len(self.instrument.channels), 6) From 945edd3e908f452ad6fd64dcaebca1fda4503d43 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Tue, 6 Jun 2017 11:34:48 +0200 Subject: [PATCH 25/49] More tests of adding channels --- qcodes/tests/test_channels.py | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/qcodes/tests/test_channels.py b/qcodes/tests/test_channels.py index d7044db257ff..72d9e562c1e7 100644 --- a/qcodes/tests/test_channels.py +++ b/qcodes/tests/test_channels.py @@ -57,12 +57,13 @@ def test_channel_access_is_identical(self, value, channel): # that currently does not support set. def test_add_channel(self): + n_channels = len(self.instrument.channels) name = 'foo' channel = DummyChannel(self.instrument, 'Chan'+name, name) self.instrument.channels.append(channel) self.instrument.add_submodule(name, channel) - self.assertEqual(len(self.instrument.channels), 7) + self.assertEqual(len(self.instrument.channels), n_channels+1) self.instrument.channels.lock() # after locking the channels it's not possible to add any more channels @@ -70,7 +71,7 @@ def test_add_channel(self): name = 'bar' channel = DummyChannel(self.instrument, 'Chan' + name, name) self.instrument.channels.append(channel) - self.instrument.add_submodule(name, channel) + self.assertEqual(len(self.instrument.channels), n_channels + 1) def test_add_channels_from_generator(self): n_channels = len(self.instrument.channels) @@ -80,6 +81,30 @@ def test_add_channels_from_generator(self): self.assertEqual(len(self.instrument.channels), n_channels + len(names)) + def test_add_channels_from_tuple(self): + n_channels = len(self.instrument.channels) + names = ('foo', 'bar', 'foobar') + channels = tuple(DummyChannel(self.instrument, 'Chan'+name, name) for name in names) + self.instrument.channels.extend(channels) + + self.assertEqual(len(self.instrument.channels), n_channels + len(names)) + + def test_insert_channel(self): + n_channels = len(self.instrument.channels) + name = 'foo' + channel = DummyChannel(self.instrument, 'Chan'+name, name) + self.instrument.channels.insert(1, channel) + self.instrument.add_submodule(name, channel) + + self.assertEqual(len(self.instrument.channels), n_channels+1) + self.assertIs(self.instrument.channels[1], channel) + self.instrument.channels.lock() + # after locking the channels it's not possible to add any more channels + with self.assertRaises(AttributeError): + name = 'bar' + channel = DummyChannel(self.instrument, 'Chan' + name, name) + self.instrument.channels.insert(2, channel) + self.assertEqual(len(self.instrument.channels), n_channels + 1) @given(setpoints=hst.lists(hst.floats(0, 300), min_size=4, max_size=4)) def test_combine_channels(self, setpoints): From 347c0b2c8bc4f848c7bbe637bdd135b30551d773 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Tue, 6 Jun 2017 14:04:59 +0200 Subject: [PATCH 26/49] Add missing functions and parameters to channellist --- qcodes/instrument/channel.py | 30 +++++++++++++++++++++++------- qcodes/tests/test_channels.py | 4 +++- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index 37855c3e99d9..d9100499c908 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -110,11 +110,13 @@ class ChannelList(Metadatable): Attributes: parameters (Dict[Parameter]): All the parameters supported by this group of channels. + This is implemented as a reference to the parameters in the first channel so if you + dynamically modify one or more channels this will not be accurate. functions (Dict[Function]): All the functions supported by this group of channels """ - def __init__(self, parent, name, chan_type, chan_list=None, snapshotable=True): + def __init__(self, parent, name, chan_type: type, chan_list=None, snapshotable=True): super().__init__() self._parent = parent @@ -124,6 +126,8 @@ def __init__(self, parent, name, chan_type, chan_list=None, snapshotable=True): self._chan_type = chan_type self._snapshotable = snapshotable + self.parameters = {} + self.functions = {} # If a list of channels is not provided, define a list to store channels. # This will eventually become a locked tuple. if chan_list is None: @@ -132,11 +136,11 @@ def __init__(self, parent, name, chan_type, chan_list=None, snapshotable=True): else: self._locked = True self._channels = tuple(chan_list) + self.parameters = self._channels[0].parameters + self.functions = self._channels[0].functions if not all(isinstance(chan, chan_type) for chan in self._channels): raise TypeError("All items in this channel list must be of type {}.".format(chan_type.__name__)) - - def __getitem__(self, i): """ Return either a single channel, or a new ChannelList containing only the specified channels @@ -178,7 +182,7 @@ def __add__(self, other): return ChannelList(self._parent, self._name, self._chan_type, self._channels + other._channels) - def append(self, obj): + def append(self, obj: InstrumentChannel): """ When initially constructing the channel list, a new channel to add to the end of the list @@ -191,9 +195,13 @@ def append(self, obj): raise TypeError("All items in a channel list must be of the same type." " Adding {} to a list of {}.".format(type(obj).__name__, self._chan_type.__name__)) + if not self.parameters: + self.parameters = obj.parameters + if not self.functions: + self.functions = obj.functions return self._channels.append(obj) - def extend(self, objects): + def extend(self, objects: InstrumentChannel): """ Insert an iterable of objects into the list of channels. @@ -207,9 +215,13 @@ def extend(self, objects): raise AttributeError("Cannot extend a locked channel list") if not all(isinstance(obj, self._chan_type) for obj in objects): raise TypeError("All items in a channel list must be of the same type.") + if not self.parameters: + self.parameters = objects[0].parameters + if not self.functions: + self.functions = objects[0].functions return self._channels.extend(objects) - def index(self, obj): + def index(self, obj: InstrumentChannel): """ Return the index of the given object @@ -218,7 +230,7 @@ def index(self, obj): """ return self._channels.index(obj) - def insert(self, index, obj): + def insert(self, index, obj: InstrumentChannel): """ Insert an object into the channel list at a specific index. @@ -233,6 +245,10 @@ def insert(self, index, obj): raise TypeError("All items in a channel list must be of the same type." " Adding {} to a list of {}.".format(type(obj).__name__, self._chan_type.__name__)) + if not self.parameters: + self.parameters = obj.parameters + if not self.functions: + self.functions = obj.functions return self._channels.insert(index, obj) def lock(self): diff --git a/qcodes/tests/test_channels.py b/qcodes/tests/test_channels.py index 72d9e562c1e7..f5e1666b0486 100644 --- a/qcodes/tests/test_channels.py +++ b/qcodes/tests/test_channels.py @@ -124,9 +124,11 @@ def test_combine_channels(self, setpoints): expected = tuple(setpoints[0:2] + [0, 0] + setpoints[2:]) self.assertEquals(self.instrument.channels.temperature(), expected) + def test_channel_parameters(self): + self.assertTrue('temperature' in self.instrument.channels.parameters) + self.assertEqual(len(self.instrument.channels.parameters), 1) class TestChannelsLoop(TestCase): - pass def setUp(self): self.instrument = DummyChannelInstrument(name='testchanneldummy') From ff8a3161bc8825a2bb062611316c7c00fc9764a7 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Tue, 6 Jun 2017 14:21:15 +0200 Subject: [PATCH 27/49] fix: function testS --- qcodes/tests/test_channels.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/qcodes/tests/test_channels.py b/qcodes/tests/test_channels.py index f5e1666b0486..cfe2d746e4b7 100644 --- a/qcodes/tests/test_channels.py +++ b/qcodes/tests/test_channels.py @@ -2,6 +2,7 @@ import unittest from qcodes.tests.instrument_mocks import DummyChannelInstrument, DummyChannel +from qcodes.instrument.channel import ChannelList from qcodes.utils.validators import Numbers from qcodes.instrument.parameter import ManualParameter @@ -125,8 +126,17 @@ def test_combine_channels(self, setpoints): self.assertEquals(self.instrument.channels.temperature(), expected) def test_channel_parameters(self): - self.assertTrue('temperature' in self.instrument.channels.parameters) - self.assertEqual(len(self.instrument.channels.parameters), 1) + self.assertTrue('temperature' in self.instrument.channels.parameters) + self.assertEqual(len(self.instrument.channels.parameters), 1) + + + def test_channel_functions(self): + dc = DummyChannel(self.instrument, 'Chan' + 'bar', 'bar') + dc.add_function('dummyfunc', call_cmd='foobar') + self.assertTrue('dummyfunc' in dc.functions) + dcl = ChannelList(self.instrument, 'DummyChannelList', DummyChannel) + dcl.append(dc) + self.assertTrue('dummyfunc' in dcl.functions) class TestChannelsLoop(TestCase): From 2a3aa626753de57fc8ccd45747ca204bf36e4ecb Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Wed, 7 Jun 2017 10:35:12 +0200 Subject: [PATCH 28/49] assert to unittest style --- qcodes/tests/test_channels.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qcodes/tests/test_channels.py b/qcodes/tests/test_channels.py index cfe2d746e4b7..7975f464a3f7 100644 --- a/qcodes/tests/test_channels.py +++ b/qcodes/tests/test_channels.py @@ -114,10 +114,10 @@ def test_combine_channels(self, setpoints): mychannels = self.instrument.channels[0:2] + self.instrument.channels[4:] self.assertEqual(len(mychannels), 4) - assert mychannels[0] is self.instrument.A - assert mychannels[1] is self.instrument.B - assert mychannels[2] is self.instrument.E - assert mychannels[3] is self.instrument.F + self.assertIs(mychannels[0], self.instrument.A) + self.assertIs(mychannels[1], self.instrument.B) + self.assertIs(mychannels[2], self.instrument.E) + self.assertIs(mychannels[3], self.instrument.F) for i in range(len(mychannels)): mychannels[i].temperature(setpoints[i]) From bc2b4afd344b18a8972d4d2cc9087edbca1f242b Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Wed, 7 Jun 2017 10:37:49 +0200 Subject: [PATCH 29/49] Revert "fix: function testS" This reverts commit ff8a3161bc8825a2bb062611316c7c00fc9764a7. --- qcodes/tests/test_channels.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/qcodes/tests/test_channels.py b/qcodes/tests/test_channels.py index 7975f464a3f7..f407e5723d51 100644 --- a/qcodes/tests/test_channels.py +++ b/qcodes/tests/test_channels.py @@ -2,7 +2,6 @@ import unittest from qcodes.tests.instrument_mocks import DummyChannelInstrument, DummyChannel -from qcodes.instrument.channel import ChannelList from qcodes.utils.validators import Numbers from qcodes.instrument.parameter import ManualParameter @@ -126,17 +125,8 @@ def test_combine_channels(self, setpoints): self.assertEquals(self.instrument.channels.temperature(), expected) def test_channel_parameters(self): - self.assertTrue('temperature' in self.instrument.channels.parameters) - self.assertEqual(len(self.instrument.channels.parameters), 1) - - - def test_channel_functions(self): - dc = DummyChannel(self.instrument, 'Chan' + 'bar', 'bar') - dc.add_function('dummyfunc', call_cmd='foobar') - self.assertTrue('dummyfunc' in dc.functions) - dcl = ChannelList(self.instrument, 'DummyChannelList', DummyChannel) - dcl.append(dc) - self.assertTrue('dummyfunc' in dcl.functions) + self.assertTrue('temperature' in self.instrument.channels.parameters) + self.assertEqual(len(self.instrument.channels.parameters), 1) class TestChannelsLoop(TestCase): From d0a22c5918a7310e0a6e4d9e95c3b200c0dcf1e5 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Wed, 7 Jun 2017 10:37:58 +0200 Subject: [PATCH 30/49] Revert "Add missing functions and parameters to channellist" This reverts commit 347c0b2c8bc4f848c7bbe637bdd135b30551d773. --- qcodes/instrument/channel.py | 30 +++++++----------------------- qcodes/tests/test_channels.py | 4 +--- 2 files changed, 8 insertions(+), 26 deletions(-) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index d9100499c908..37855c3e99d9 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -110,13 +110,11 @@ class ChannelList(Metadatable): Attributes: parameters (Dict[Parameter]): All the parameters supported by this group of channels. - This is implemented as a reference to the parameters in the first channel so if you - dynamically modify one or more channels this will not be accurate. functions (Dict[Function]): All the functions supported by this group of channels """ - def __init__(self, parent, name, chan_type: type, chan_list=None, snapshotable=True): + def __init__(self, parent, name, chan_type, chan_list=None, snapshotable=True): super().__init__() self._parent = parent @@ -126,8 +124,6 @@ def __init__(self, parent, name, chan_type: type, chan_list=None, snapshotable=T self._chan_type = chan_type self._snapshotable = snapshotable - self.parameters = {} - self.functions = {} # If a list of channels is not provided, define a list to store channels. # This will eventually become a locked tuple. if chan_list is None: @@ -136,11 +132,11 @@ def __init__(self, parent, name, chan_type: type, chan_list=None, snapshotable=T else: self._locked = True self._channels = tuple(chan_list) - self.parameters = self._channels[0].parameters - self.functions = self._channels[0].functions if not all(isinstance(chan, chan_type) for chan in self._channels): raise TypeError("All items in this channel list must be of type {}.".format(chan_type.__name__)) + + def __getitem__(self, i): """ Return either a single channel, or a new ChannelList containing only the specified channels @@ -182,7 +178,7 @@ def __add__(self, other): return ChannelList(self._parent, self._name, self._chan_type, self._channels + other._channels) - def append(self, obj: InstrumentChannel): + def append(self, obj): """ When initially constructing the channel list, a new channel to add to the end of the list @@ -195,13 +191,9 @@ def append(self, obj: InstrumentChannel): raise TypeError("All items in a channel list must be of the same type." " Adding {} to a list of {}.".format(type(obj).__name__, self._chan_type.__name__)) - if not self.parameters: - self.parameters = obj.parameters - if not self.functions: - self.functions = obj.functions return self._channels.append(obj) - def extend(self, objects: InstrumentChannel): + def extend(self, objects): """ Insert an iterable of objects into the list of channels. @@ -215,13 +207,9 @@ def extend(self, objects: InstrumentChannel): raise AttributeError("Cannot extend a locked channel list") if not all(isinstance(obj, self._chan_type) for obj in objects): raise TypeError("All items in a channel list must be of the same type.") - if not self.parameters: - self.parameters = objects[0].parameters - if not self.functions: - self.functions = objects[0].functions return self._channels.extend(objects) - def index(self, obj: InstrumentChannel): + def index(self, obj): """ Return the index of the given object @@ -230,7 +218,7 @@ def index(self, obj: InstrumentChannel): """ return self._channels.index(obj) - def insert(self, index, obj: InstrumentChannel): + def insert(self, index, obj): """ Insert an object into the channel list at a specific index. @@ -245,10 +233,6 @@ def insert(self, index, obj: InstrumentChannel): raise TypeError("All items in a channel list must be of the same type." " Adding {} to a list of {}.".format(type(obj).__name__, self._chan_type.__name__)) - if not self.parameters: - self.parameters = obj.parameters - if not self.functions: - self.functions = obj.functions return self._channels.insert(index, obj) def lock(self): diff --git a/qcodes/tests/test_channels.py b/qcodes/tests/test_channels.py index f407e5723d51..b103cc8251e2 100644 --- a/qcodes/tests/test_channels.py +++ b/qcodes/tests/test_channels.py @@ -124,11 +124,9 @@ def test_combine_channels(self, setpoints): expected = tuple(setpoints[0:2] + [0, 0] + setpoints[2:]) self.assertEquals(self.instrument.channels.temperature(), expected) - def test_channel_parameters(self): - self.assertTrue('temperature' in self.instrument.channels.parameters) - self.assertEqual(len(self.instrument.channels.parameters), 1) class TestChannelsLoop(TestCase): + pass def setUp(self): self.instrument = DummyChannelInstrument(name='testchanneldummy') From 6999f62fb6525fda7b5c094bea6df7ad1a6a118b Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Wed, 7 Jun 2017 11:05:22 +0200 Subject: [PATCH 31/49] Remove docs of non existing attributes --- qcodes/instrument/channel.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index 37855c3e99d9..575c59d7251e 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -108,10 +108,6 @@ class ChannelList(Metadatable): This is used when objects stored inside a channel list are accessible in multiple ways and should not be repeated in an instrument snapshot. - Attributes: - parameters (Dict[Parameter]): All the parameters supported by this group of channels. - - functions (Dict[Function]): All the functions supported by this group of channels """ def __init__(self, parent, name, chan_type, chan_list=None, snapshotable=True): From 3fa48b788439c59966faddbfccc1f3ded01f6de0 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Wed, 7 Jun 2017 11:14:49 +0200 Subject: [PATCH 32/49] Docs: decadac quote *IDN to remove warning --- qcodes/instrument_drivers/Harvard/Decadac.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qcodes/instrument_drivers/Harvard/Decadac.py b/qcodes/instrument_drivers/Harvard/Decadac.py index 487f64de2e92..edeeb55db39b 100644 --- a/qcodes/instrument_drivers/Harvard/Decadac.py +++ b/qcodes/instrument_drivers/Harvard/Decadac.py @@ -435,7 +435,7 @@ def ramp_all(self, volt, ramp_rate): def get_idn(self): """ - Attempt to identify the dac. Since we don't have standard SCPI commands, *IDN will + Attempt to identify the dac. Since we don't have standard SCPI commands, ``*IDN`` will do nothing on this DAC. Returns: @@ -447,7 +447,7 @@ def get_idn(self): def connect_message(self, idn_param='IDN', begin_time=None): """ - Print a connect message, taking into account the lack of a standard *IDN on + Print a connect message, taking into account the lack of a standard ``*IDN`` on the Harvard DAC Args: From 2dbacc0d5e367d659570552729017ce1b4607428 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Tue, 6 Jun 2017 17:19:25 +0200 Subject: [PATCH 33/49] Add some channel notebooks --- docs/examples/Channel experiments.ipynb | 1360 +++++++++++++++++ ...nnel instrument with array parameter.ipynb | 541 +++++++ 2 files changed, 1901 insertions(+) create mode 100644 docs/examples/Channel experiments.ipynb create mode 100644 docs/examples/Channel instrument with array parameter.ipynb diff --git a/docs/examples/Channel experiments.ipynb b/docs/examples/Channel experiments.ipynb new file mode 100644 index 000000000000..06d96fd36acd --- /dev/null +++ b/docs/examples/Channel experiments.ipynb @@ -0,0 +1,1360 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "User schema at /Users/jhn/qcodesrc_schema.json not found.User settings won't be validated\n" + ] + } + ], + "source": [ + "from unittest import TestCase\n", + "import unittest\n", + "\n", + "from qcodes.tests.instrument_mocks import DummyChannelInstrument, DummyChannel\n", + "from qcodes.utils.validators import Numbers\n", + "from qcodes.instrument.parameter import ManualParameter\n", + "from qcodes.loops import Loop" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "instrument = DummyChannelInstrument(name='testchanneldummy')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Started at 2017-06-06 13:34:53\n", + "DataSet:\n", + " location = 'data/2017-06-06/#010_{name}_13-34-53'\n", + " | | | \n", + " Setpoint | p1_set | p1 | (201,)\n", + " Measured | testchanneldummy_ChanA_temperature_0_0 | testchanneldummy_ChanA_temperature | (201,)\n", + " Measured | testchanneldummy_ChanB_temperature | testchanneldummy_ChanB_temperature | (201,)\n", + " Measured | testchanneldummy_ChanC_temperature | testchanneldummy_ChanC_temperature | (201,)\n", + " Measured | testchanneldummy_ChanD_temperature | testchanneldummy_ChanD_temperature | (201,)\n", + " Measured | testchanneldummy_ChanE_temperature | testchanneldummy_ChanE_temperature | (201,)\n", + " Measured | testchanneldummy_ChanF_temperature | testchanneldummy_ChanF_temperature | (201,)\n", + " Measured | testchanneldummy_ChanA_temperature_1 | temperature | (201,)\n", + "Finished at 2017-06-06 13:34:54\n" + ] + } + ], + "source": [ + "p1 = ManualParameter(name='p1', vals=Numbers(-10, 10))\n", + "loop = Loop(p1.sweep(-10,10,0.1), 0.001).each(instrument.channels.temperature,\n", + " instrument.channels[0].temperature)\n", + "data = loop.run()\n", + "#self.assertEqual(data.p1_set.ndarray.shape, (201, ))\n", + "#self.assertEqual(data.testchanneldummy_testchanneldummy_ChanA_temperature.ndarray.shape, (201, ))\n", + "# loop = Loop(p1.sweep(-10,10,0.1), 0.001).each(instrument.channels[0].temperature)\n", + "# data = loop.run()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "('testchanneldummy_ChanA_temperature',\n", + " 'testchanneldummy_ChanB_temperature',\n", + " 'testchanneldummy_ChanC_temperature',\n", + " 'testchanneldummy_ChanD_temperature',\n", + " 'testchanneldummy_ChanE_temperature',\n", + " 'testchanneldummy_ChanF_temperature')" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "instrument.channels.temperature.names" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'temperature'" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "instrument.A.temperature.name" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'testchanneldummy_ChanA_temperature'" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "instrument.A.temperature.full_name" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'testchanneldummy_ChanA'" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "instrument.A.temperature._instrument.name" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'testchanneldummy_ChanA'" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "instrument.channels[0].temperature._instrument.name" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'testchanneldummy'" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "instrument.channels.temperature._instrument.name" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "mcip = instrument.channels.temperature" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'testchanneldummy_Multi_temperature'" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mcip.full_name" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "('testchanneldummy_ChanA_temperature',\n", + " 'testchanneldummy_ChanB_temperature',\n", + " 'testchanneldummy_ChanC_temperature',\n", + " 'testchanneldummy_ChanD_temperature',\n", + " 'testchanneldummy_ChanE_temperature',\n", + " 'testchanneldummy_ChanF_temperature')" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mcip.names" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "('testchanneldummy_ChanA_temperature',\n", + " 'testchanneldummy_ChanB_temperature',\n", + " 'testchanneldummy_ChanC_temperature',\n", + " 'testchanneldummy_ChanD_temperature',\n", + " 'testchanneldummy_ChanE_temperature',\n", + " 'testchanneldummy_ChanF_temperature')" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mcip.full_names" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "p1 = ManualParameter(name='p1', vals=Numbers(-10, 10))" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "loop = Loop(p1.sweep(-10,10,1), 1e-6).each(instrument.channels.temperature)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:negative delay -0.000000 sec\n", + "WARNING:root:negative delay -0.000000 sec\n", + "WARNING:root:negative delay -0.000000 sec\n", + "WARNING:root:negative delay -0.000001 sec\n", + "WARNING:root:negative delay -0.000001 sec\n", + "WARNING:root:negative delay -0.000001 sec\n", + "WARNING:root:negative delay -0.000001 sec\n", + "WARNING:root:negative delay -0.000001 sec\n", + "WARNING:root:negative delay -0.000000 sec\n", + "WARNING:root:negative delay -0.000000 sec\n", + "WARNING:root:negative delay -0.000000 sec\n", + "WARNING:root:negative delay -0.000001 sec\n", + "WARNING:root:negative delay -0.000001 sec\n", + "WARNING:root:negative delay -0.000001 sec\n", + "WARNING:root:negative delay -0.000001 sec\n", + "WARNING:root:negative delay -0.000001 sec\n", + "WARNING:root:negative delay -0.000001 sec\n", + "WARNING:root:negative delay -0.000001 sec\n", + "WARNING:root:negative delay -0.000002 sec\n", + "WARNING:root:negative delay -0.000002 sec\n", + "WARNING:root:negative delay -0.000000 sec\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Started at 2017-06-06 13:34:57\n", + "DataSet:\n", + " location = 'data/2017-06-06/#011_{name}_13-34-57'\n", + " | | | \n", + " Setpoint | p1_set | p1 | (21,)\n", + " Measured | testchanneldummy_ChanA_temperature | testchanneldummy_ChanA_temperature | (21,)\n", + " Measured | testchanneldummy_ChanB_temperature | testchanneldummy_ChanB_temperature | (21,)\n", + " Measured | testchanneldummy_ChanC_temperature | testchanneldummy_ChanC_temperature | (21,)\n", + " Measured | testchanneldummy_ChanD_temperature | testchanneldummy_ChanD_temperature | (21,)\n", + " Measured | testchanneldummy_ChanE_temperature | testchanneldummy_ChanE_temperature | (21,)\n", + " Measured | testchanneldummy_ChanF_temperature | testchanneldummy_ChanF_temperature | (21,)\n", + "Finished at 2017-06-06 13:34:57\n" + ] + } + ], + "source": [ + "sata =loop.run() " + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['__class__',\n", + " '__delattr__',\n", + " '__dict__',\n", + " '__dir__',\n", + " '__doc__',\n", + " '__eq__',\n", + " '__format__',\n", + " '__ge__',\n", + " '__getattr__',\n", + " '__getattribute__',\n", + " '__gt__',\n", + " '__hash__',\n", + " '__init__',\n", + " '__le__',\n", + " '__lt__',\n", + " '__module__',\n", + " '__ne__',\n", + " '__new__',\n", + " '__reduce__',\n", + " '__reduce_ex__',\n", + " '__repr__',\n", + " '__setattr__',\n", + " '__sizeof__',\n", + " '__str__',\n", + " '__subclasshook__',\n", + " '__weakref__',\n", + " '_clean_array_ids',\n", + " '_clean_param_ids',\n", + " 'action_id_map',\n", + " 'add_array',\n", + " 'add_metadata',\n", + " 'arrays',\n", + " 'background_functions',\n", + " 'complete',\n", + " 'default_formatter',\n", + " 'default_io',\n", + " 'default_parameter_array',\n", + " 'default_parameter_name',\n", + " 'delegate_attr_dicts',\n", + " 'delegate_attr_objects',\n", + " 'finalize',\n", + " 'formatter',\n", + " 'fraction_complete',\n", + " 'get_array_metadata',\n", + " 'get_changes',\n", + " 'io',\n", + " 'last_store',\n", + " 'last_write',\n", + " 'location',\n", + " 'location_provider',\n", + " 'metadata',\n", + " 'omit_delegate_attrs',\n", + " 'p1_set',\n", + " 'read',\n", + " 'read_metadata',\n", + " 'save_metadata',\n", + " 'snapshot',\n", + " 'store',\n", + " 'sync',\n", + " 'testchanneldummy_ChanA_temperature',\n", + " 'testchanneldummy_ChanB_temperature',\n", + " 'testchanneldummy_ChanC_temperature',\n", + " 'testchanneldummy_ChanD_temperature',\n", + " 'testchanneldummy_ChanE_temperature',\n", + " 'testchanneldummy_ChanF_temperature',\n", + " 'write',\n", + " 'write_copy',\n", + " 'write_period']" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dir(sata)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'__class__': 'qcodes.data.data_set.DataSet',\n", + " 'arrays': {'p1_set': {'__class__': 'qcodes.data.data_array.DataArray',\n", + " 'action_indices': (),\n", + " 'array_id': 'p1_set',\n", + " 'is_setpoint': True,\n", + " 'label': 'p1',\n", + " 'name': 'p1',\n", + " 'shape': (21,),\n", + " 'unit': '',\n", + " 'vals': ''},\n", + " 'testchanneldummy_ChanA_temperature': {'__class__': 'qcodes.data.data_array.DataArray',\n", + " 'action_indices': (0, 0),\n", + " 'array_id': 'testchanneldummy_ChanA_temperature',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannelInstrument',\n", + " 'instrument_name': 'testchanneldummy',\n", + " 'is_setpoint': False,\n", + " 'label': 'Temperature_A',\n", + " 'labels': ('Temperature_A',\n", + " 'Temperature_B',\n", + " 'Temperature_C',\n", + " 'Temperature_D',\n", + " 'Temperature_E',\n", + " 'Temperature_F'),\n", + " 'name': 'testchanneldummy_ChanA_temperature',\n", + " 'names': ('testchanneldummy_ChanA_temperature',\n", + " 'testchanneldummy_ChanB_temperature',\n", + " 'testchanneldummy_ChanC_temperature',\n", + " 'testchanneldummy_ChanD_temperature',\n", + " 'testchanneldummy_ChanE_temperature',\n", + " 'testchanneldummy_ChanF_temperature'),\n", + " 'setpoint_labels': None,\n", + " 'setpoint_names': None,\n", + " 'setpoint_units': None,\n", + " 'shape': (21,),\n", + " 'unit': 'K',\n", + " 'units': ('K', 'K', 'K', 'K', 'K', 'K')},\n", + " 'testchanneldummy_ChanB_temperature': {'__class__': 'qcodes.data.data_array.DataArray',\n", + " 'action_indices': (0, 1),\n", + " 'array_id': 'testchanneldummy_ChanB_temperature',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannelInstrument',\n", + " 'instrument_name': 'testchanneldummy',\n", + " 'is_setpoint': False,\n", + " 'label': 'Temperature_B',\n", + " 'labels': ('Temperature_A',\n", + " 'Temperature_B',\n", + " 'Temperature_C',\n", + " 'Temperature_D',\n", + " 'Temperature_E',\n", + " 'Temperature_F'),\n", + " 'name': 'testchanneldummy_ChanB_temperature',\n", + " 'names': ('testchanneldummy_ChanA_temperature',\n", + " 'testchanneldummy_ChanB_temperature',\n", + " 'testchanneldummy_ChanC_temperature',\n", + " 'testchanneldummy_ChanD_temperature',\n", + " 'testchanneldummy_ChanE_temperature',\n", + " 'testchanneldummy_ChanF_temperature'),\n", + " 'setpoint_labels': None,\n", + " 'setpoint_names': None,\n", + " 'setpoint_units': None,\n", + " 'shape': (21,),\n", + " 'unit': 'K',\n", + " 'units': ('K', 'K', 'K', 'K', 'K', 'K')},\n", + " 'testchanneldummy_ChanC_temperature': {'__class__': 'qcodes.data.data_array.DataArray',\n", + " 'action_indices': (0, 2),\n", + " 'array_id': 'testchanneldummy_ChanC_temperature',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannelInstrument',\n", + " 'instrument_name': 'testchanneldummy',\n", + " 'is_setpoint': False,\n", + " 'label': 'Temperature_C',\n", + " 'labels': ('Temperature_A',\n", + " 'Temperature_B',\n", + " 'Temperature_C',\n", + " 'Temperature_D',\n", + " 'Temperature_E',\n", + " 'Temperature_F'),\n", + " 'name': 'testchanneldummy_ChanC_temperature',\n", + " 'names': ('testchanneldummy_ChanA_temperature',\n", + " 'testchanneldummy_ChanB_temperature',\n", + " 'testchanneldummy_ChanC_temperature',\n", + " 'testchanneldummy_ChanD_temperature',\n", + " 'testchanneldummy_ChanE_temperature',\n", + " 'testchanneldummy_ChanF_temperature'),\n", + " 'setpoint_labels': None,\n", + " 'setpoint_names': None,\n", + " 'setpoint_units': None,\n", + " 'shape': (21,),\n", + " 'unit': 'K',\n", + " 'units': ('K', 'K', 'K', 'K', 'K', 'K')},\n", + " 'testchanneldummy_ChanD_temperature': {'__class__': 'qcodes.data.data_array.DataArray',\n", + " 'action_indices': (0, 3),\n", + " 'array_id': 'testchanneldummy_ChanD_temperature',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannelInstrument',\n", + " 'instrument_name': 'testchanneldummy',\n", + " 'is_setpoint': False,\n", + " 'label': 'Temperature_D',\n", + " 'labels': ('Temperature_A',\n", + " 'Temperature_B',\n", + " 'Temperature_C',\n", + " 'Temperature_D',\n", + " 'Temperature_E',\n", + " 'Temperature_F'),\n", + " 'name': 'testchanneldummy_ChanD_temperature',\n", + " 'names': ('testchanneldummy_ChanA_temperature',\n", + " 'testchanneldummy_ChanB_temperature',\n", + " 'testchanneldummy_ChanC_temperature',\n", + " 'testchanneldummy_ChanD_temperature',\n", + " 'testchanneldummy_ChanE_temperature',\n", + " 'testchanneldummy_ChanF_temperature'),\n", + " 'setpoint_labels': None,\n", + " 'setpoint_names': None,\n", + " 'setpoint_units': None,\n", + " 'shape': (21,),\n", + " 'unit': 'K',\n", + " 'units': ('K', 'K', 'K', 'K', 'K', 'K')},\n", + " 'testchanneldummy_ChanE_temperature': {'__class__': 'qcodes.data.data_array.DataArray',\n", + " 'action_indices': (0, 4),\n", + " 'array_id': 'testchanneldummy_ChanE_temperature',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannelInstrument',\n", + " 'instrument_name': 'testchanneldummy',\n", + " 'is_setpoint': False,\n", + " 'label': 'Temperature_E',\n", + " 'labels': ('Temperature_A',\n", + " 'Temperature_B',\n", + " 'Temperature_C',\n", + " 'Temperature_D',\n", + " 'Temperature_E',\n", + " 'Temperature_F'),\n", + " 'name': 'testchanneldummy_ChanE_temperature',\n", + " 'names': ('testchanneldummy_ChanA_temperature',\n", + " 'testchanneldummy_ChanB_temperature',\n", + " 'testchanneldummy_ChanC_temperature',\n", + " 'testchanneldummy_ChanD_temperature',\n", + " 'testchanneldummy_ChanE_temperature',\n", + " 'testchanneldummy_ChanF_temperature'),\n", + " 'setpoint_labels': None,\n", + " 'setpoint_names': None,\n", + " 'setpoint_units': None,\n", + " 'shape': (21,),\n", + " 'unit': 'K',\n", + " 'units': ('K', 'K', 'K', 'K', 'K', 'K')},\n", + " 'testchanneldummy_ChanF_temperature': {'__class__': 'qcodes.data.data_array.DataArray',\n", + " 'action_indices': (0, 5),\n", + " 'array_id': 'testchanneldummy_ChanF_temperature',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannelInstrument',\n", + " 'instrument_name': 'testchanneldummy',\n", + " 'is_setpoint': False,\n", + " 'label': 'Temperature_F',\n", + " 'labels': ('Temperature_A',\n", + " 'Temperature_B',\n", + " 'Temperature_C',\n", + " 'Temperature_D',\n", + " 'Temperature_E',\n", + " 'Temperature_F'),\n", + " 'name': 'testchanneldummy_ChanF_temperature',\n", + " 'names': ('testchanneldummy_ChanA_temperature',\n", + " 'testchanneldummy_ChanB_temperature',\n", + " 'testchanneldummy_ChanC_temperature',\n", + " 'testchanneldummy_ChanD_temperature',\n", + " 'testchanneldummy_ChanE_temperature',\n", + " 'testchanneldummy_ChanF_temperature'),\n", + " 'setpoint_labels': None,\n", + " 'setpoint_names': None,\n", + " 'setpoint_units': None,\n", + " 'shape': (21,),\n", + " 'unit': 'K',\n", + " 'units': ('K', 'K', 'K', 'K', 'K', 'K')}},\n", + " 'formatter': 'qcodes.data.gnuplot_format.GNUPlotFormat',\n", + " 'io': \"\",\n", + " 'location': 'data/2017-06-06/#011_{name}_13-34-57',\n", + " 'loop': {'__class__': 'qcodes.loops.ActiveLoop',\n", + " 'actions': [{'__class__': 'qcodes.instrument.channel.MultiChannelInstrumentParameter',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannelInstrument',\n", + " 'instrument_name': 'testchanneldummy',\n", + " 'labels': ('Temperature_A',\n", + " 'Temperature_B',\n", + " 'Temperature_C',\n", + " 'Temperature_D',\n", + " 'Temperature_E',\n", + " 'Temperature_F'),\n", + " 'name': 'Multi_temperature',\n", + " 'names': ('testchanneldummy_ChanA_temperature',\n", + " 'testchanneldummy_ChanB_temperature',\n", + " 'testchanneldummy_ChanC_temperature',\n", + " 'testchanneldummy_ChanD_temperature',\n", + " 'testchanneldummy_ChanE_temperature',\n", + " 'testchanneldummy_ChanF_temperature'),\n", + " 'setpoint_labels': None,\n", + " 'setpoint_names': None,\n", + " 'setpoint_units': None,\n", + " 'ts': None,\n", + " 'units': ('K', 'K', 'K', 'K', 'K', 'K'),\n", + " 'value': None}],\n", + " 'delay': 1e-06,\n", + " 'sweep_values': {'parameter': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", + " 'label': 'p1',\n", + " 'name': 'p1',\n", + " 'ts': None,\n", + " 'unit': '',\n", + " 'vals': '',\n", + " 'value': None},\n", + " 'values': [{'first': -10.0, 'last': 10.0, 'num': 21, 'type': 'linear'}]},\n", + " 'then_actions': [],\n", + " 'ts_end': '2017-06-06 13:34:57',\n", + " 'ts_start': '2017-06-06 13:34:57',\n", + " 'use_threads': False}}" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sata.metadata" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'__class__': 'qcodes.tests.instrument_mocks.DummyChannelInstrument',\n", + " 'functions': {},\n", + " 'name': 'testchanneldummy',\n", + " 'parameters': {'IDN': {'__class__': 'qcodes.instrument.parameter.StandardParameter',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannelInstrument',\n", + " 'instrument_name': 'testchanneldummy',\n", + " 'label': 'IDN',\n", + " 'name': 'IDN',\n", + " 'ts': None,\n", + " 'unit': '',\n", + " 'vals': '',\n", + " 'value': None}},\n", + " 'submodules': {'A': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'functions': {},\n", + " 'name': 'testchanneldummy_ChanA',\n", + " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'instrument_name': 'testchanneldummy_ChanA',\n", + " 'label': 'Temperature_A',\n", + " 'name': 'temperature',\n", + " 'ts': '2017-06-06 13:34:53',\n", + " 'unit': 'K',\n", + " 'vals': '',\n", + " 'value': 0}},\n", + " 'submodules': {}},\n", + " 'B': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'functions': {},\n", + " 'name': 'testchanneldummy_ChanB',\n", + " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'instrument_name': 'testchanneldummy_ChanB',\n", + " 'label': 'Temperature_B',\n", + " 'name': 'temperature',\n", + " 'ts': '2017-06-06 13:34:53',\n", + " 'unit': 'K',\n", + " 'vals': '',\n", + " 'value': 0}},\n", + " 'submodules': {}},\n", + " 'C': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'functions': {},\n", + " 'name': 'testchanneldummy_ChanC',\n", + " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'instrument_name': 'testchanneldummy_ChanC',\n", + " 'label': 'Temperature_C',\n", + " 'name': 'temperature',\n", + " 'ts': '2017-06-06 13:34:53',\n", + " 'unit': 'K',\n", + " 'vals': '',\n", + " 'value': 0}},\n", + " 'submodules': {}},\n", + " 'D': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'functions': {},\n", + " 'name': 'testchanneldummy_ChanD',\n", + " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'instrument_name': 'testchanneldummy_ChanD',\n", + " 'label': 'Temperature_D',\n", + " 'name': 'temperature',\n", + " 'ts': '2017-06-06 13:34:53',\n", + " 'unit': 'K',\n", + " 'vals': '',\n", + " 'value': 0}},\n", + " 'submodules': {}},\n", + " 'E': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'functions': {},\n", + " 'name': 'testchanneldummy_ChanE',\n", + " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'instrument_name': 'testchanneldummy_ChanE',\n", + " 'label': 'Temperature_E',\n", + " 'name': 'temperature',\n", + " 'ts': '2017-06-06 13:34:53',\n", + " 'unit': 'K',\n", + " 'vals': '',\n", + " 'value': 0}},\n", + " 'submodules': {}},\n", + " 'F': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'functions': {},\n", + " 'name': 'testchanneldummy_ChanF',\n", + " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'instrument_name': 'testchanneldummy_ChanF',\n", + " 'label': 'Temperature_F',\n", + " 'name': 'temperature',\n", + " 'ts': '2017-06-06 13:34:53',\n", + " 'unit': 'K',\n", + " 'vals': '',\n", + " 'value': 0}},\n", + " 'submodules': {}},\n", + " 'channels': {'__class__': 'qcodes.instrument.channel.ChannelList',\n", + " 'snapshotable': False}}}" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "instrument.snapshot()" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'temperature': }" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "instrument.channels.parameters" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "qcodes.tests.instrument_mocks.DummyChannel" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "instrument.channels._chan_type" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'temperature': }" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "instrument.A.parameters" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'channels' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0minstrument\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mchannels\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mextend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchannels\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mNameError\u001b[0m: name 'channels' is not defined" + ] + } + ], + "source": [ + "instrument.channels.extend(channels)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + " at 0x10da5cf68>" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(DummyChannel(instrument, name, name) for name in ['g', 'h', 'i'])" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[,\n", + " ,\n", + " ]" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list((DummyChannel(instrument, name, name) for name in ['g', 'h', 'i']))" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "instrument.channels.extend((DummyChannel(instrument, name, name) for name in ['g', 'h', 'i']))" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "ChannelList(, DummyChannel, [, , , , , , , , ])" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "instrument.channels" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'__class__': 'qcodes.instrument.channel.ChannelList', 'snapshotable': False}" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "instrument.channels.snapshot_base()" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "instrument.channels._snapshotable = True" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'__class__': 'qcodes.tests.instrument_mocks.DummyChannelInstrument',\n", + " 'functions': {},\n", + " 'name': 'testchanneldummy',\n", + " 'parameters': {'IDN': {'__class__': 'qcodes.instrument.parameter.StandardParameter',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannelInstrument',\n", + " 'instrument_name': 'testchanneldummy',\n", + " 'label': 'IDN',\n", + " 'name': 'IDN',\n", + " 'ts': None,\n", + " 'unit': '',\n", + " 'vals': '',\n", + " 'value': None}},\n", + " 'submodules': {'A': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'functions': {},\n", + " 'name': 'testchanneldummy_ChanA',\n", + " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'instrument_name': 'testchanneldummy_ChanA',\n", + " 'label': 'Temperature_A',\n", + " 'name': 'temperature',\n", + " 'ts': '2017-06-06 13:34:53',\n", + " 'unit': 'K',\n", + " 'vals': '',\n", + " 'value': 0}},\n", + " 'submodules': {}},\n", + " 'B': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'functions': {},\n", + " 'name': 'testchanneldummy_ChanB',\n", + " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'instrument_name': 'testchanneldummy_ChanB',\n", + " 'label': 'Temperature_B',\n", + " 'name': 'temperature',\n", + " 'ts': '2017-06-06 13:34:53',\n", + " 'unit': 'K',\n", + " 'vals': '',\n", + " 'value': 0}},\n", + " 'submodules': {}},\n", + " 'C': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'functions': {},\n", + " 'name': 'testchanneldummy_ChanC',\n", + " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'instrument_name': 'testchanneldummy_ChanC',\n", + " 'label': 'Temperature_C',\n", + " 'name': 'temperature',\n", + " 'ts': '2017-06-06 13:34:53',\n", + " 'unit': 'K',\n", + " 'vals': '',\n", + " 'value': 0}},\n", + " 'submodules': {}},\n", + " 'D': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'functions': {},\n", + " 'name': 'testchanneldummy_ChanD',\n", + " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'instrument_name': 'testchanneldummy_ChanD',\n", + " 'label': 'Temperature_D',\n", + " 'name': 'temperature',\n", + " 'ts': '2017-06-06 13:34:53',\n", + " 'unit': 'K',\n", + " 'vals': '',\n", + " 'value': 0}},\n", + " 'submodules': {}},\n", + " 'E': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'functions': {},\n", + " 'name': 'testchanneldummy_ChanE',\n", + " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'instrument_name': 'testchanneldummy_ChanE',\n", + " 'label': 'Temperature_E',\n", + " 'name': 'temperature',\n", + " 'ts': '2017-06-06 13:34:53',\n", + " 'unit': 'K',\n", + " 'vals': '',\n", + " 'value': 0}},\n", + " 'submodules': {}},\n", + " 'F': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'functions': {},\n", + " 'name': 'testchanneldummy_ChanF',\n", + " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'instrument_name': 'testchanneldummy_ChanF',\n", + " 'label': 'Temperature_F',\n", + " 'name': 'temperature',\n", + " 'ts': '2017-06-06 13:34:53',\n", + " 'unit': 'K',\n", + " 'vals': '',\n", + " 'value': 0}},\n", + " 'submodules': {}},\n", + " 'channels': {'__class__': 'qcodes.instrument.channel.ChannelList',\n", + " 'channels': {'testchanneldummy_ChanA': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'functions': {},\n", + " 'name': 'testchanneldummy_ChanA',\n", + " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'instrument_name': 'testchanneldummy_ChanA',\n", + " 'label': 'Temperature_A',\n", + " 'name': 'temperature',\n", + " 'ts': '2017-06-06 13:34:53',\n", + " 'unit': 'K',\n", + " 'vals': '',\n", + " 'value': 0}},\n", + " 'submodules': {}},\n", + " 'testchanneldummy_ChanB': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'functions': {},\n", + " 'name': 'testchanneldummy_ChanB',\n", + " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'instrument_name': 'testchanneldummy_ChanB',\n", + " 'label': 'Temperature_B',\n", + " 'name': 'temperature',\n", + " 'ts': '2017-06-06 13:34:53',\n", + " 'unit': 'K',\n", + " 'vals': '',\n", + " 'value': 0}},\n", + " 'submodules': {}},\n", + " 'testchanneldummy_ChanC': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'functions': {},\n", + " 'name': 'testchanneldummy_ChanC',\n", + " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'instrument_name': 'testchanneldummy_ChanC',\n", + " 'label': 'Temperature_C',\n", + " 'name': 'temperature',\n", + " 'ts': '2017-06-06 13:34:53',\n", + " 'unit': 'K',\n", + " 'vals': '',\n", + " 'value': 0}},\n", + " 'submodules': {}},\n", + " 'testchanneldummy_ChanD': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'functions': {},\n", + " 'name': 'testchanneldummy_ChanD',\n", + " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'instrument_name': 'testchanneldummy_ChanD',\n", + " 'label': 'Temperature_D',\n", + " 'name': 'temperature',\n", + " 'ts': '2017-06-06 13:34:53',\n", + " 'unit': 'K',\n", + " 'vals': '',\n", + " 'value': 0}},\n", + " 'submodules': {}},\n", + " 'testchanneldummy_ChanE': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'functions': {},\n", + " 'name': 'testchanneldummy_ChanE',\n", + " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'instrument_name': 'testchanneldummy_ChanE',\n", + " 'label': 'Temperature_E',\n", + " 'name': 'temperature',\n", + " 'ts': '2017-06-06 13:34:53',\n", + " 'unit': 'K',\n", + " 'vals': '',\n", + " 'value': 0}},\n", + " 'submodules': {}},\n", + " 'testchanneldummy_ChanF': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'functions': {},\n", + " 'name': 'testchanneldummy_ChanF',\n", + " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'instrument_name': 'testchanneldummy_ChanF',\n", + " 'label': 'Temperature_F',\n", + " 'name': 'temperature',\n", + " 'ts': '2017-06-06 13:34:53',\n", + " 'unit': 'K',\n", + " 'vals': '',\n", + " 'value': 0}},\n", + " 'submodules': {}},\n", + " 'testchanneldummy_g': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'functions': {},\n", + " 'name': 'testchanneldummy_g',\n", + " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'instrument_name': 'testchanneldummy_g',\n", + " 'label': 'Temperature_g',\n", + " 'name': 'temperature',\n", + " 'ts': '2017-06-06 13:34:59',\n", + " 'unit': 'K',\n", + " 'vals': '',\n", + " 'value': 0}},\n", + " 'submodules': {}},\n", + " 'testchanneldummy_h': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'functions': {},\n", + " 'name': 'testchanneldummy_h',\n", + " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'instrument_name': 'testchanneldummy_h',\n", + " 'label': 'Temperature_h',\n", + " 'name': 'temperature',\n", + " 'ts': '2017-06-06 13:34:59',\n", + " 'unit': 'K',\n", + " 'vals': '',\n", + " 'value': 0}},\n", + " 'submodules': {}},\n", + " 'testchanneldummy_i': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'functions': {},\n", + " 'name': 'testchanneldummy_i',\n", + " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", + " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", + " 'instrument_name': 'testchanneldummy_i',\n", + " 'label': 'Temperature_i',\n", + " 'name': 'temperature',\n", + " 'ts': '2017-06-06 13:34:59',\n", + " 'unit': 'K',\n", + " 'vals': '',\n", + " 'value': 0}},\n", + " 'submodules': {}}},\n", + " 'snapshotable': True}}}" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "instrument.snapshot()" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'IDN': }" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "instrument.parameters" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'temperature': }" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "instrument.channels.parameters" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'temperature': }" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "instrument.A.parameters" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Temperature_A'" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "instrument.channels.parameters['temperature']" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "'temperature' in instrument.channels.parameters" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(instrument.channels.parameters)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/examples/Channel instrument with array parameter.ipynb b/docs/examples/Channel instrument with array parameter.ipynb new file mode 100644 index 000000000000..b9c64d17694b --- /dev/null +++ b/docs/examples/Channel instrument with array parameter.ipynb @@ -0,0 +1,541 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "User schema at /Users/jhn/qcodesrc_schema.json not found.User settings won't be validated\n" + ] + } + ], + "source": [ + "from qcodes import InstrumentChannel, Instrument, ChannelList\n", + "from qcodes import ManualParameter, MultiParameter, ArrayParameter\n", + "from qcodes.utils.validators import Numbers\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "class DummyChannel(InstrumentChannel):\n", + " \"\"\"\n", + " A single dummy channel implementation\n", + " \"\"\"\n", + "\n", + " def __init__(self, parent, name, channel):\n", + " super().__init__(parent, name)\n", + "\n", + " self._channel = channel\n", + "\n", + " # Add the various channel parameters\n", + " self.add_parameter('temperature',\n", + " parameter_class=ManualParameter,\n", + " initial_value=0,\n", + " label=\"Temperature_{}\".format(channel),\n", + " unit='K',\n", + " vals=Numbers(0, 300))\n", + " \n", + " self.add_parameter(name='foobar',\n", + " parameter_class=MultiSetPointParam)\n", + " \n", + " self.add_parameter(name='myarray',\n", + " parameter_class=ArraySetPointParam)\n", + "\n", + "\n", + "class DummyChannelInstrument(Instrument):\n", + " \"\"\"\n", + " Dummy instrument with channels\n", + " \"\"\"\n", + "\n", + " def __init__(self, name, **kwargs):\n", + " super().__init__(name, **kwargs)\n", + "\n", + " channels = ChannelList(self, \"TempSensors\", DummyChannel, snapshotable=False)\n", + " for chan_name in ('A', 'B', 'C', 'D', 'E', 'F'):\n", + " channel = DummyChannel(self, 'Chan{}'.format(chan_name), chan_name)\n", + " channels.append(channel)\n", + " self.add_submodule(chan_name, channel)\n", + " self.add_submodule(\"channels\", channels)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "class MultiSetPointParam(MultiParameter):\n", + " \"\"\"\n", + " Multiparameter which only purpose it to test that units, setpoints\n", + " and so on are copied correctly to the individual arrays in the datarray.\n", + " \"\"\"\n", + " def __init__(self, instrument, name):\n", + " name = 'testparameter'\n", + " shapes = ((5,), (5,))\n", + " names = ('this', 'that')\n", + " labels = ('this label', 'that label')\n", + " units = ('this unit', 'that unit')\n", + " sp_base = tuple(np.linspace(5, 9, 5))\n", + " setpoints = ((sp_base,), (sp_base,))\n", + " setpoint_names = (('this_setpoint',), ('this_setpoint',))\n", + " setpoint_labels = (('this setpoint',), ('this setpoint',))\n", + " setpoint_units = (('this setpointunit',), ('this setpointunit',))\n", + " super().__init__(name, names, shapes,\n", + " labels=labels,\n", + " units=units,\n", + " setpoints=setpoints,\n", + " setpoint_labels=setpoint_labels,\n", + " setpoint_names=setpoint_names,\n", + " setpoint_units=setpoint_units)\n", + "\n", + " def get(self):\n", + " return np.zeros(5), np.ones(5)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class ArraySetPointParam(ArrayParameter):\n", + " \"\"\"\n", + " Multiparameter which only purpose it to test that units, setpoints\n", + " and so on are copied correctly to the individual arrays in the datarray.\n", + " \"\"\"\n", + " def __init__(self, instrument, name):\n", + " name = 'testparameter'\n", + " shape = (5,)\n", + " label = 'this label'\n", + " unit = 'this unit'\n", + " sp_base = tuple(np.linspace(5, 9, 5))\n", + " setpoints = (sp_base,)\n", + " setpoint_names = ('this_setpoint',)\n", + " setpoint_labels = ('this setpoint',)\n", + " setpoint_units = ('this setpointunit',)\n", + " super().__init__(name,\n", + " shape,\n", + " label=label,\n", + " unit=unit,\n", + " setpoints=setpoints,\n", + " setpoint_labels=setpoint_labels,\n", + " setpoint_names=setpoint_names,\n", + " setpoint_units=setpoint_units)\n", + "\n", + " def get(self):\n", + " return np.random.rand(5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "inst = DummyChannelInstrument('foobar')" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "inst.channels[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from qcodes import Loop" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Started at 2017-06-06 17:11:20\n", + "DataSet:\n", + " location = 'data/2017-06-06/#020_{name}_17-11-20'\n", + " | | | \n", + " Setpoint | foobar_ChanA_temperature_set | temperature | (11,)\n", + " Setpoint | this_setpoint_set | this_setpoint | (11, 5)\n", + " Measured | this | this | (11, 5)\n", + " Measured | that | that | (11, 5)\n", + "Finished at 2017-06-06 17:11:31\n" + ] + } + ], + "source": [ + "data1 = Loop(inst.A.temperature.sweep(0,10,1), 1).each(inst.A.foobar).run()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "DataArray[11,5]: this\n", + "array([[ 0., 0., 0., 0., 0.],\n", + " [ 0., 0., 0., 0., 0.],\n", + " [ 0., 0., 0., 0., 0.],\n", + " [ 0., 0., 0., 0., 0.],\n", + " [ 0., 0., 0., 0., 0.],\n", + " [ 0., 0., 0., 0., 0.],\n", + " [ 0., 0., 0., 0., 0.],\n", + " [ 0., 0., 0., 0., 0.],\n", + " [ 0., 0., 0., 0., 0.],\n", + " [ 0., 0., 0., 0., 0.],\n", + " [ 0., 0., 0., 0., 0.]])" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "data1.this" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Started at 2017-06-06 17:11:33\n", + "DataSet:\n", + " location = 'data/2017-06-06/#021_{name}_17-11-33'\n", + " | | | \n", + " Setpoint | foobar_ChanA_temperature_set | temperature | (11,)\n", + " Setpoint | this_setpoint_set | this_setpoint | (11, 5)\n", + " Measured | this | this | (11, 5)\n", + " Measured | that | that | (11, 5)\n", + "Finished at 2017-06-06 17:11:44\n" + ] + } + ], + "source": [ + "data2 = Loop(inst.A.temperature.sweep(0,10,1), 1).each(inst.channels[0].foobar).run()" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'MultiSetPointParam' object has no attribute 'label'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mdata2\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mLoop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minst\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mA\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtemperature\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msweep\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0meach\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minst\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mchannels\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfoobar\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m~/src/Qcodes/qcodes/instrument/channel.py\u001b[0m in \u001b[0;36m__getattr__\u001b[0;34m(self, name)\u001b[0m\n\u001b[1;32m 299\u001b[0m \u001b[0mnames\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"{}_{}\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchan\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 300\u001b[0m \u001b[0mshapes\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# TODO: Pull shapes intelligently\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 301\u001b[0;31m \u001b[0mlabels\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchan\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mparameters\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlabel\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 302\u001b[0m \u001b[0munits\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchan\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mparameters\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0munit\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 303\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/src/Qcodes/qcodes/instrument/channel.py\u001b[0m in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 299\u001b[0m \u001b[0mnames\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"{}_{}\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchan\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 300\u001b[0m \u001b[0mshapes\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# TODO: Pull shapes intelligently\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 301\u001b[0;31m \u001b[0mlabels\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchan\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mparameters\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlabel\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 302\u001b[0m \u001b[0munits\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchan\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mparameters\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0munit\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 303\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mAttributeError\u001b[0m: 'MultiSetPointParam' object has no attribute 'label'" + ] + } + ], + "source": [ + "data2 = Loop(inst.A.temperature.sweep(0,10,1), 1).each(inst.channels[0:1].foobar).run()" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Started at 2017-06-06 17:13:16\n", + "DataSet:\n", + " location = 'data/2017-06-06/#023_{name}_17-13-16'\n", + " | | | \n", + " Setpoint | foobar_ChanA_temperature_set | temperature | (11,)\n", + " Measured | testparameter | testparameter | (11, 5)\n", + "Finished at 2017-06-06 17:13:27\n" + ] + } + ], + "source": [ + "data3 = Loop(inst.A.temperature.sweep(0,10,1), 1).each(inst.A.myarray).run()" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Started at 2017-06-06 17:14:09\n", + "DataSet:\n", + " location = 'data/2017-06-06/#024_{name}_17-14-09'\n", + " | | | \n", + " Setpoint | foobar_ChanA_temperature_set | temperature | (11,)\n", + " Measured | testparameter | testparameter | (11, 5)\n", + "Finished at 2017-06-06 17:14:20\n" + ] + } + ], + "source": [ + "data3 = Loop(inst.A.temperature.sweep(0,10,1), 1).each(inst.channels[0].myarray).run()" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Started at 2017-06-06 17:14:32\n", + "DataSet:\n", + " location = 'data/2017-06-06/#025_{name}_17-14-32'\n", + " | | | \n", + " Setpoint | foobar_ChanA_temperature_set | temperature | (11,)\n", + " Measured | foobar_ChanA_myarray | foobar_ChanA_myarray | (11,)\n", + "Finished at 2017-06-06 17:14:33\n" + ] + }, + { + "ename": "ValueError", + "evalue": "setting an array element with a sequence.", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mdata3\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mLoop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minst\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mA\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtemperature\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msweep\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0meach\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minst\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mchannels\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmyarray\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m~/src/Qcodes/qcodes/loops.py\u001b[0m in \u001b[0;36mrun\u001b[0;34m(self, use_threads, quiet, station, progress_interval, *args, **kwargs)\u001b[0m\n\u001b[1;32m 717\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mquiet\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 718\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdatetime\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnow\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstrftime\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Started at %Y-%m-%d %H:%M:%S'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 719\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_run_wrapper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 720\u001b[0m \u001b[0mds\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata_set\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 721\u001b[0m \u001b[0;32mfinally\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/src/Qcodes/qcodes/loops.py\u001b[0m in \u001b[0;36m_run_wrapper\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 764\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_run_wrapper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 765\u001b[0m \u001b[0;31m# try:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 766\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_run_loop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 767\u001b[0m \u001b[0;31m# finally:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 768\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mhasattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'data_set'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/src/Qcodes/qcodes/loops.py\u001b[0m in \u001b[0;36m_run_loop\u001b[0;34m(self, first_delay, action_indices, loop_indices, current_values, **ignore_kwargs)\u001b[0m\n\u001b[1;32m 836\u001b[0m f(first_delay=delay,\n\u001b[1;32m 837\u001b[0m \u001b[0mloop_indices\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mnew_indices\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 838\u001b[0;31m current_values=new_values)\n\u001b[0m\u001b[1;32m 839\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 840\u001b[0m \u001b[0;31m# after the first action, no delay is inherited\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/src/Qcodes/qcodes/actions.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, loop_indices, **ignore_kwargs)\u001b[0m\n\u001b[1;32m 144\u001b[0m \u001b[0mout_dict\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mparam_id\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mparam_out\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 145\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 146\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstore\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mloop_indices\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mout_dict\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 147\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 148\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/src/Qcodes/qcodes/data/data_set.py\u001b[0m in \u001b[0;36mstore\u001b[0;34m(self, loop_indices, ids_values)\u001b[0m\n\u001b[1;32m 384\u001b[0m \"\"\"\n\u001b[1;32m 385\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0marray_id\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalue\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mids_values\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mitems\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 386\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marrays\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0marray_id\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mloop_indices\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 387\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlast_store\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtime\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtime\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 388\u001b[0m if (self.write_period is not None and\n", + "\u001b[0;32m~/src/Qcodes/qcodes/data/data_array.py\u001b[0m in \u001b[0;36m__setitem__\u001b[0;34m(self, loop_indices, value)\u001b[0m\n\u001b[1;32m 339\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_update_modified_range\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmin_li\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmax_li\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 340\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 341\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mndarray\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__setitem__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mloop_indices\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 342\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 343\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__getitem__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mloop_indices\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mValueError\u001b[0m: setting an array element with a sequence." + ] + } + ], + "source": [ + "data3 = Loop(inst.A.temperature.sweep(0,10,1), 1).each(inst.channels[0:1].myarray).run()" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(array([ 0.16087586, 0.63407137, 0.40978536, 0.76905476, 0.82876632]),\n", + " array([ 0.69882021, 0.73425924, 0.25545122, 0.86773728, 0.10338811]),\n", + " array([ 0.8698955 , 0.3354928 , 0.07489469, 0.38043035, 0.70433896]),\n", + " array([ 0.45961196, 0.92813073, 0.5977839 , 0.35455607, 0.06801803]),\n", + " array([ 0.49382892, 0.90160562, 0.36905395, 0.33856894, 0.39967096]),\n", + " array([ 0.8050116 , 0.74691103, 0.65995307, 0.85211923, 0.57228292]))" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "inst.channels.myarray.get()" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'MultiSetPointParam' object has no attribute 'label'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0minst\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mchannels\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfoobar\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m~/src/Qcodes/qcodes/instrument/channel.py\u001b[0m in \u001b[0;36m__getattr__\u001b[0;34m(self, name)\u001b[0m\n\u001b[1;32m 299\u001b[0m \u001b[0mnames\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"{}_{}\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchan\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 300\u001b[0m \u001b[0mshapes\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# TODO: Pull shapes intelligently\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 301\u001b[0;31m \u001b[0mlabels\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchan\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mparameters\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlabel\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 302\u001b[0m \u001b[0munits\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchan\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mparameters\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0munit\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 303\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/src/Qcodes/qcodes/instrument/channel.py\u001b[0m in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 299\u001b[0m \u001b[0mnames\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"{}_{}\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchan\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 300\u001b[0m \u001b[0mshapes\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# TODO: Pull shapes intelligently\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 301\u001b[0;31m \u001b[0mlabels\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchan\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mparameters\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlabel\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 302\u001b[0m \u001b[0munits\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchan\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mparameters\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0munit\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 303\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mAttributeError\u001b[0m: 'MultiSetPointParam' object has no attribute 'label'" + ] + } + ], + "source": [ + "inst.channels.foobar.get()" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "('this label',\n", + " 'this label',\n", + " 'this label',\n", + " 'this label',\n", + " 'this label',\n", + " 'this label')" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "inst.channels.myarray.labels" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'this label'" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "inst.A.myarray.label" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(5,)" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "inst.A.myarray.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "((), (), (), (), (), ())" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "inst.channels.myarray.shapes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From cd23b071a15582107db4ee577c6c4a8f9b848731 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Wed, 7 Jun 2017 13:53:53 +0200 Subject: [PATCH 34/49] fix: test_channels small tweeks --- qcodes/tests/test_channels.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/qcodes/tests/test_channels.py b/qcodes/tests/test_channels.py index b103cc8251e2..7cfadc3e41d9 100644 --- a/qcodes/tests/test_channels.py +++ b/qcodes/tests/test_channels.py @@ -118,15 +118,14 @@ def test_combine_channels(self, setpoints): self.assertIs(mychannels[2], self.instrument.E) self.assertIs(mychannels[3], self.instrument.F) - for i in range(len(mychannels)): - mychannels[i].temperature(setpoints[i]) + for i, chan in enumerate(mychannels): + chan.temperature(setpoints[i]) expected = tuple(setpoints[0:2] + [0, 0] + setpoints[2:]) self.assertEquals(self.instrument.channels.temperature(), expected) class TestChannelsLoop(TestCase): - pass def setUp(self): self.instrument = DummyChannelInstrument(name='testchanneldummy') @@ -148,7 +147,7 @@ def test_loop_measure_all_channels(self): data = loop.run() self.assertEqual(data.p1_set.ndarray.shape, (21, )) self.assertEqual(len(data.arrays), 7) - for i, chan in enumerate(['A', 'B', 'C', 'D', 'E', 'F']): + for chan in ['A', 'B', 'C', 'D', 'E', 'F']: self.assertEqual(getattr(data, 'testchanneldummy_Chan{}_temperature'.format(chan)).ndarray.shape, (21,)) def test_loop_measure_channels_individually(self): @@ -159,7 +158,7 @@ def test_loop_measure_channels_individually(self): self.instrument.channels[3].temperature) data = loop.run() self.assertEqual(data.p1_set.ndarray.shape, (21, )) - for i, chan in enumerate(['A', 'B', 'C', 'D']): + for chan in ['A', 'B', 'C', 'D']: self.assertEqual(getattr(data, 'testchanneldummy_Chan{}_temperature'.format(chan)).ndarray.shape, (21,)) @given(values=hst.lists(hst.floats(0, 300), min_size=4, max_size=4)) From 477084034103bb081d69c5108546b5cd11d50247 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Wed, 7 Jun 2017 17:38:39 +0200 Subject: [PATCH 35/49] Add support to array parameters in channels And error clearly if you try to measure multiple multiparameters --- qcodes/instrument/channel.py | 32 ++++++++++++++-- qcodes/tests/instrument_mocks.py | 40 +++++++++++++++++-- qcodes/tests/test_channels.py | 66 ++++++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+), 7 deletions(-) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index 575c59d7251e..f94c57f7051b 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -2,7 +2,7 @@ from typing import List, Tuple, Union from .base import Instrument -from .parameter import MultiParameter +from .parameter import MultiParameter, ArrayParameter from ..utils.metadata import Metadatable from ..utils.helpers import full_class @@ -274,13 +274,33 @@ def __getattr__(self, name): """ # Check if this is a valid parameter if name in self._channels[0].parameters: + setpoints = None + setpoint_names = None + setpoint_labels = None + setpoint_units = None # We need to construct a MultiParameter object to get each of the - # values our of each parameter in our list + # values our of each parameter in our list, we don't currently try to + # construct a multiparameter from a list of multi parameters + if isinstance(self._channels[0].parameters[name], MultiParameter): + raise NotImplementedError("Slicing is currently not supported for MultiParameters") names = tuple("{}_{}".format(chan.name, name) for chan in self._channels) - shapes = tuple(() for chan in self._channels) # TODO: Pull shapes intelligently labels = tuple(chan.parameters[name].label for chan in self._channels) units = tuple(chan.parameters[name].unit for chan in self._channels) + if isinstance(self._channels[0].parameters[name], ArrayParameter): + shapes = tuple(chan.parameters[name].shape for chan in self._channels) + + if self._channels[0].parameters[name].setpoints: + setpoints = tuple(chan.parameters[name].setpoints for chan in self._channels) + if self._channels[0].parameters[name].setpoint_names: + setpoint_names = tuple(chan.parameters[name].setpoint_names for chan in self._channels) + if self._channels[0].parameters[name].setpoint_labels: + setpoint_labels = tuple(chan.parameters[name].setpoint_labels for chan in self._channels) + if self._channels[0].parameters[name].setpoint_units: + setpoint_units = tuple(chan.parameters[name].setpoint_units for chan in self._channels) + else: + shapes = tuple(() for chan in self._channels) + param = MultiChannelInstrumentParameter(self._channels, param_name=name, name="Multi_{}".format(name), @@ -288,7 +308,11 @@ def __getattr__(self, name): shapes=shapes, instrument=self._parent, labels=labels, - units=units) + units=units, + setpoints=setpoints, + setpoint_names=setpoint_names, + setpoint_units=setpoint_units, + setpoint_labels=setpoint_labels) return param # Check if this is a valid function diff --git a/qcodes/tests/instrument_mocks.py b/qcodes/tests/instrument_mocks.py index bfdb1e2afdb8..49edc94a660f 100644 --- a/qcodes/tests/instrument_mocks.py +++ b/qcodes/tests/instrument_mocks.py @@ -2,7 +2,7 @@ from qcodes.instrument.base import Instrument from qcodes.utils.validators import Numbers -from qcodes.instrument.parameter import MultiParameter, ManualParameter +from qcodes.instrument.parameter import MultiParameter, ManualParameter, ArrayParameter from qcodes.instrument.channel import InstrumentChannel, ChannelList class MockParabola(Instrument): @@ -125,6 +125,12 @@ def __init__(self, parent, name, channel): unit='K', vals=Numbers(0, 300)) + self.add_parameter(name='dummy_multi_parameter', + parameter_class=MultiSetPointParam) + + self.add_parameter(name='dummy_array_parameter', + parameter_class=ArraySetPointParam) + class DummyChannelInstrument(Instrument): """ Dummy instrument with channels @@ -170,8 +176,7 @@ class MultiSetPointParam(MultiParameter): Multiparameter which only purpose it to test that units, setpoints and so on are copied correctly to the individual arrays in the datarray. """ - def __init__(self): - name = 'testparameter' + def __init__(self, instrument='DummyInst', name='testparameter'): shapes = ((5,), (5,)) names = ('this', 'that') labels = ('this label', 'that label') @@ -191,3 +196,32 @@ def __init__(self): def get(self): return np.zeros(5), np.ones(5) + +class ArraySetPointParam(ArrayParameter): + """ + Arrayparameter which only purpose it to test that units, setpoints + and so on are copied correctly to the individual arrays in the datarray. + """ + + def __init__(self, instrument, name): + name = 'testparameter' + shape = (5,) + label = 'this label' + unit = 'this unit' + sp_base = tuple(np.linspace(5, 9, 5)) + setpoints = (sp_base,) + setpoint_names = ('this_setpoint',) + setpoint_labels = ('this setpoint',) + setpoint_units = ('this setpointunit',) + super().__init__(name, + shape, + label=label, + unit=unit, + setpoints=setpoints, + setpoint_labels=setpoint_labels, + setpoint_names=setpoint_names, + setpoint_units=setpoint_units) + + def get(self): + return np.ones(5) + 1 + diff --git a/qcodes/tests/test_channels.py b/qcodes/tests/test_channels.py index 7cfadc3e41d9..04a56fd57bc9 100644 --- a/qcodes/tests/test_channels.py +++ b/qcodes/tests/test_channels.py @@ -204,5 +204,71 @@ def test_nested_loop_over_channels(self, loop_channels, measure_channel): '{}_temperature_set'.format(channel_to_label[loop_channels[1]])).ndarray assert_allclose(array, expected_array) + def test_loop_slicing_multiparameter_raises(self): + with self.assertRaises(NotImplementedError): + loop = Loop(self.instrument.A.temperature.sweep(0, 10, 1), 0.1) + loop.each(self.instrument.channels[0:2].dummy_multi_parameter).run() + + def test_loop_multiparameter_by_name(self): + loop = Loop(self.instrument.A.temperature.sweep(0, 10, 1), 0.1) + data = loop.each(self.instrument.A.dummy_multi_parameter).run() + self.assertIn('this_setpoint_set', data.arrays.keys()) + assert_array_equal(data.arrays['this_setpoint_set'].ndarray, + np.repeat(np.arange(5., 10).reshape(1, 5), 11, axis=0)) + self.assertIn('this', data.arrays.keys()) + assert_array_equal(data.arrays['this'].ndarray, np.zeros((11, 5))) + self.assertIn('that', data.arrays.keys()) + assert_array_equal(data.arrays['that'].ndarray, np.ones((11, 5))) + self.assertIn('testchanneldummy_ChanA_temperature_set', data.arrays.keys()) + assert_array_equal(data.arrays['testchanneldummy_ChanA_temperature_set'].ndarray, np.arange(0, 10.1, 1)) + + def test_loop_multiparameter_by_index(self): + loop = Loop(self.instrument.channels[0].temperature.sweep(0, 10, 1), 0.1) + data = loop.each(self.instrument.A.dummy_multi_parameter).run() + self.assertIn('this_setpoint_set', data.arrays.keys()) + assert_array_equal(data.arrays['this_setpoint_set'].ndarray, + np.repeat(np.arange(5., 10).reshape(1, 5), 11, axis=0)) + self.assertIn('this', data.arrays.keys()) + assert_array_equal(data.arrays['this'].ndarray, np.zeros((11, 5))) + self.assertIn('that', data.arrays.keys()) + assert_array_equal(data.arrays['that'].ndarray, np.ones((11, 5))) + self.assertIn('testchanneldummy_ChanA_temperature_set', data.arrays.keys()) + assert_array_equal(data.arrays['testchanneldummy_ChanA_temperature_set'].ndarray, np.arange(0, 10.1, 1)) + + def test_loop_slicing_arrayparameter(self): + loop = Loop(self.instrument.A.temperature.sweep(0, 10, 1), 0.1) + data = loop.each(self.instrument.channels[0:2].dummy_array_parameter).run() + self.assertIn('this_setpoint_set', data.arrays.keys()) + assert_array_equal(data.arrays['this_setpoint_set'].ndarray, + np.repeat(np.arange(5., 10).reshape(1, 5), 11, axis=0)) + self.assertIn('testchanneldummy_ChanA_dummy_array_parameter', data.arrays.keys()) + assert_array_equal(data.arrays['testchanneldummy_ChanA_dummy_array_parameter'].ndarray, np.ones((11, 5))+1) + self.assertIn('testchanneldummy_ChanB_dummy_array_parameter', data.arrays.keys()) + assert_array_equal(data.arrays['testchanneldummy_ChanB_dummy_array_parameter'].ndarray, np.ones((11, 5))+1) + self.assertIn('testchanneldummy_ChanA_temperature_set', data.arrays.keys()) + assert_array_equal(data.arrays['testchanneldummy_ChanA_temperature_set'].ndarray, np.arange(0, 10.1, 1)) + + def test_loop_arrayparameter_by_name(self): + loop = Loop(self.instrument.A.temperature.sweep(0, 10, 1), 0.1) + data = loop.each(self.instrument.A.dummy_array_parameter).run() + self.assertIn('this_setpoint_set', data.arrays.keys()) + assert_array_equal(data.arrays['this_setpoint_set'].ndarray, + np.repeat(np.arange(5., 10).reshape(1, 5), 11, axis=0)) + self.assertIn('testparameter', data.arrays.keys()) + assert_array_equal(data.arrays['testparameter'].ndarray, np.ones((11, 5))+1) + self.assertIn('testchanneldummy_ChanA_temperature_set', data.arrays.keys()) + assert_array_equal(data.arrays['testchanneldummy_ChanA_temperature_set'].ndarray, np.arange(0, 10.1, 1)) + + def test_loop_arrayparameter_by_index(self): + loop = Loop(self.instrument.channels[0].temperature.sweep(0, 10, 1), 0.1) + data = loop.each(self.instrument.A.dummy_array_parameter).run() + self.assertIn('this_setpoint_set', data.arrays.keys()) + assert_array_equal(data.arrays['this_setpoint_set'].ndarray, + np.repeat(np.arange(5., 10).reshape(1, 5), 11, axis=0)) + self.assertIn('testparameter', data.arrays.keys()) + assert_array_equal(data.arrays['testparameter'].ndarray, np.ones((11, 5)) + 1) + self.assertIn('testchanneldummy_ChanA_temperature_set', data.arrays.keys()) + assert_array_equal(data.arrays['testchanneldummy_ChanA_temperature_set'].ndarray, np.arange(0, 10.1, 1)) + if __name__ == '__main__': unittest.main() From 4b051cbc205bf986b02f4b6d902f61f123b650f5 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Thu, 8 Jun 2017 13:10:31 +0200 Subject: [PATCH 36/49] Mock parameters add instruments to fix names in tests --- qcodes/tests/instrument_mocks.py | 7 ++++--- qcodes/tests/test_channels.py | 24 ++++++++++++------------ 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/qcodes/tests/instrument_mocks.py b/qcodes/tests/instrument_mocks.py index 49edc94a660f..c010a5b62c15 100644 --- a/qcodes/tests/instrument_mocks.py +++ b/qcodes/tests/instrument_mocks.py @@ -176,7 +176,7 @@ class MultiSetPointParam(MultiParameter): Multiparameter which only purpose it to test that units, setpoints and so on are copied correctly to the individual arrays in the datarray. """ - def __init__(self, instrument='DummyInst', name='testparameter'): + def __init__(self, instrument=None, name='testparameter'): shapes = ((5,), (5,)) names = ('this', 'that') labels = ('this label', 'that label') @@ -187,6 +187,7 @@ def __init__(self, instrument='DummyInst', name='testparameter'): setpoint_labels = (('this setpoint',), ('this setpoint',)) setpoint_units = (('this setpointunit',), ('this setpointunit',)) super().__init__(name, names, shapes, + instrument=instrument, labels=labels, units=units, setpoints=setpoints, @@ -203,8 +204,7 @@ class ArraySetPointParam(ArrayParameter): and so on are copied correctly to the individual arrays in the datarray. """ - def __init__(self, instrument, name): - name = 'testparameter' + def __init__(self, instrument=None, name='testparameter'): shape = (5,) label = 'this label' unit = 'this unit' @@ -215,6 +215,7 @@ def __init__(self, instrument, name): setpoint_units = ('this setpointunit',) super().__init__(name, shape, + instrument, label=label, unit=unit, setpoints=setpoints, diff --git a/qcodes/tests/test_channels.py b/qcodes/tests/test_channels.py index 04a56fd57bc9..111a948d7e72 100644 --- a/qcodes/tests/test_channels.py +++ b/qcodes/tests/test_channels.py @@ -215,10 +215,10 @@ def test_loop_multiparameter_by_name(self): self.assertIn('this_setpoint_set', data.arrays.keys()) assert_array_equal(data.arrays['this_setpoint_set'].ndarray, np.repeat(np.arange(5., 10).reshape(1, 5), 11, axis=0)) - self.assertIn('this', data.arrays.keys()) - assert_array_equal(data.arrays['this'].ndarray, np.zeros((11, 5))) - self.assertIn('that', data.arrays.keys()) - assert_array_equal(data.arrays['that'].ndarray, np.ones((11, 5))) + self.assertIn('testchanneldummy_ChanA_this', data.arrays.keys()) + assert_array_equal(data.arrays['testchanneldummy_ChanA_this'].ndarray, np.zeros((11, 5))) + self.assertIn('testchanneldummy_ChanA_that', data.arrays.keys()) + assert_array_equal(data.arrays['testchanneldummy_ChanA_that'].ndarray, np.ones((11, 5))) self.assertIn('testchanneldummy_ChanA_temperature_set', data.arrays.keys()) assert_array_equal(data.arrays['testchanneldummy_ChanA_temperature_set'].ndarray, np.arange(0, 10.1, 1)) @@ -228,10 +228,10 @@ def test_loop_multiparameter_by_index(self): self.assertIn('this_setpoint_set', data.arrays.keys()) assert_array_equal(data.arrays['this_setpoint_set'].ndarray, np.repeat(np.arange(5., 10).reshape(1, 5), 11, axis=0)) - self.assertIn('this', data.arrays.keys()) - assert_array_equal(data.arrays['this'].ndarray, np.zeros((11, 5))) - self.assertIn('that', data.arrays.keys()) - assert_array_equal(data.arrays['that'].ndarray, np.ones((11, 5))) + self.assertIn('testchanneldummy_ChanA_this', data.arrays.keys()) + assert_array_equal(data.arrays['testchanneldummy_ChanA_this'].ndarray, np.zeros((11, 5))) + self.assertIn('testchanneldummy_ChanA_that', data.arrays.keys()) + assert_array_equal(data.arrays['testchanneldummy_ChanA_that'].ndarray, np.ones((11, 5))) self.assertIn('testchanneldummy_ChanA_temperature_set', data.arrays.keys()) assert_array_equal(data.arrays['testchanneldummy_ChanA_temperature_set'].ndarray, np.arange(0, 10.1, 1)) @@ -254,8 +254,8 @@ def test_loop_arrayparameter_by_name(self): self.assertIn('this_setpoint_set', data.arrays.keys()) assert_array_equal(data.arrays['this_setpoint_set'].ndarray, np.repeat(np.arange(5., 10).reshape(1, 5), 11, axis=0)) - self.assertIn('testparameter', data.arrays.keys()) - assert_array_equal(data.arrays['testparameter'].ndarray, np.ones((11, 5))+1) + self.assertIn('testchanneldummy_ChanA_dummy_array_parameter', data.arrays.keys()) + assert_array_equal(data.arrays['testchanneldummy_ChanA_dummy_array_parameter'].ndarray, np.ones((11, 5))+1) self.assertIn('testchanneldummy_ChanA_temperature_set', data.arrays.keys()) assert_array_equal(data.arrays['testchanneldummy_ChanA_temperature_set'].ndarray, np.arange(0, 10.1, 1)) @@ -265,8 +265,8 @@ def test_loop_arrayparameter_by_index(self): self.assertIn('this_setpoint_set', data.arrays.keys()) assert_array_equal(data.arrays['this_setpoint_set'].ndarray, np.repeat(np.arange(5., 10).reshape(1, 5), 11, axis=0)) - self.assertIn('testparameter', data.arrays.keys()) - assert_array_equal(data.arrays['testparameter'].ndarray, np.ones((11, 5)) + 1) + self.assertIn('testchanneldummy_ChanA_dummy_array_parameter', data.arrays.keys()) + assert_array_equal(data.arrays['testchanneldummy_ChanA_dummy_array_parameter'].ndarray, np.ones((11, 5)) + 1) self.assertIn('testchanneldummy_ChanA_temperature_set', data.arrays.keys()) assert_array_equal(data.arrays['testchanneldummy_ChanA_temperature_set'].ndarray, np.arange(0, 10.1, 1)) From 6d11b5bc79b4c7555c05bda50b4d417560afd027 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Thu, 8 Jun 2017 13:24:34 +0200 Subject: [PATCH 37/49] refactor tests to reduce code duplication --- qcodes/tests/test_channels.py | 39 ++++++++++++----------------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/qcodes/tests/test_channels.py b/qcodes/tests/test_channels.py index 111a948d7e72..849d9d4f0925 100644 --- a/qcodes/tests/test_channels.py +++ b/qcodes/tests/test_channels.py @@ -212,19 +212,15 @@ def test_loop_slicing_multiparameter_raises(self): def test_loop_multiparameter_by_name(self): loop = Loop(self.instrument.A.temperature.sweep(0, 10, 1), 0.1) data = loop.each(self.instrument.A.dummy_multi_parameter).run() + self._verify_multiparam_data(data) self.assertIn('this_setpoint_set', data.arrays.keys()) - assert_array_equal(data.arrays['this_setpoint_set'].ndarray, - np.repeat(np.arange(5., 10).reshape(1, 5), 11, axis=0)) - self.assertIn('testchanneldummy_ChanA_this', data.arrays.keys()) - assert_array_equal(data.arrays['testchanneldummy_ChanA_this'].ndarray, np.zeros((11, 5))) - self.assertIn('testchanneldummy_ChanA_that', data.arrays.keys()) - assert_array_equal(data.arrays['testchanneldummy_ChanA_that'].ndarray, np.ones((11, 5))) - self.assertIn('testchanneldummy_ChanA_temperature_set', data.arrays.keys()) - assert_array_equal(data.arrays['testchanneldummy_ChanA_temperature_set'].ndarray, np.arange(0, 10.1, 1)) def test_loop_multiparameter_by_index(self): loop = Loop(self.instrument.channels[0].temperature.sweep(0, 10, 1), 0.1) data = loop.each(self.instrument.A.dummy_multi_parameter).run() + self._verify_multiparam_data(data) + + def _verify_multiparam_data(self, data): self.assertIn('this_setpoint_set', data.arrays.keys()) assert_array_equal(data.arrays['this_setpoint_set'].ndarray, np.repeat(np.arange(5., 10).reshape(1, 5), 11, axis=0)) @@ -238,35 +234,26 @@ def test_loop_multiparameter_by_index(self): def test_loop_slicing_arrayparameter(self): loop = Loop(self.instrument.A.temperature.sweep(0, 10, 1), 0.1) data = loop.each(self.instrument.channels[0:2].dummy_array_parameter).run() - self.assertIn('this_setpoint_set', data.arrays.keys()) - assert_array_equal(data.arrays['this_setpoint_set'].ndarray, - np.repeat(np.arange(5., 10).reshape(1, 5), 11, axis=0)) - self.assertIn('testchanneldummy_ChanA_dummy_array_parameter', data.arrays.keys()) - assert_array_equal(data.arrays['testchanneldummy_ChanA_dummy_array_parameter'].ndarray, np.ones((11, 5))+1) - self.assertIn('testchanneldummy_ChanB_dummy_array_parameter', data.arrays.keys()) - assert_array_equal(data.arrays['testchanneldummy_ChanB_dummy_array_parameter'].ndarray, np.ones((11, 5))+1) - self.assertIn('testchanneldummy_ChanA_temperature_set', data.arrays.keys()) - assert_array_equal(data.arrays['testchanneldummy_ChanA_temperature_set'].ndarray, np.arange(0, 10.1, 1)) + self._verify_array_data(data, channels=('A', 'B')) def test_loop_arrayparameter_by_name(self): loop = Loop(self.instrument.A.temperature.sweep(0, 10, 1), 0.1) data = loop.each(self.instrument.A.dummy_array_parameter).run() - self.assertIn('this_setpoint_set', data.arrays.keys()) - assert_array_equal(data.arrays['this_setpoint_set'].ndarray, - np.repeat(np.arange(5., 10).reshape(1, 5), 11, axis=0)) - self.assertIn('testchanneldummy_ChanA_dummy_array_parameter', data.arrays.keys()) - assert_array_equal(data.arrays['testchanneldummy_ChanA_dummy_array_parameter'].ndarray, np.ones((11, 5))+1) - self.assertIn('testchanneldummy_ChanA_temperature_set', data.arrays.keys()) - assert_array_equal(data.arrays['testchanneldummy_ChanA_temperature_set'].ndarray, np.arange(0, 10.1, 1)) + self._verify_array_data(data) def test_loop_arrayparameter_by_index(self): loop = Loop(self.instrument.channels[0].temperature.sweep(0, 10, 1), 0.1) data = loop.each(self.instrument.A.dummy_array_parameter).run() + self._verify_array_data(data) + + def _verify_array_data(self, data, channels=('A',)): self.assertIn('this_setpoint_set', data.arrays.keys()) assert_array_equal(data.arrays['this_setpoint_set'].ndarray, np.repeat(np.arange(5., 10).reshape(1, 5), 11, axis=0)) - self.assertIn('testchanneldummy_ChanA_dummy_array_parameter', data.arrays.keys()) - assert_array_equal(data.arrays['testchanneldummy_ChanA_dummy_array_parameter'].ndarray, np.ones((11, 5)) + 1) + for channel in channels: + aname = 'testchanneldummy_Chan{}_dummy_array_parameter'.format(channel) + self.assertIn(aname, data.arrays.keys()) + assert_array_equal(data.arrays[aname].ndarray, np.ones((11, 5))+1) self.assertIn('testchanneldummy_ChanA_temperature_set', data.arrays.keys()) assert_array_equal(data.arrays['testchanneldummy_ChanA_temperature_set'].ndarray, np.arange(0, 10.1, 1)) From e1c6a5706b28291c77f6416698833d9ab61c3cda Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Thu, 8 Jun 2017 13:45:42 +0200 Subject: [PATCH 38/49] Revert "Add some channel notebooks" This reverts commit 2dbacc0d5e367d659570552729017ce1b4607428. --- docs/examples/Channel experiments.ipynb | 1360 ----------------- ...nnel instrument with array parameter.ipynb | 541 ------- 2 files changed, 1901 deletions(-) delete mode 100644 docs/examples/Channel experiments.ipynb delete mode 100644 docs/examples/Channel instrument with array parameter.ipynb diff --git a/docs/examples/Channel experiments.ipynb b/docs/examples/Channel experiments.ipynb deleted file mode 100644 index 06d96fd36acd..000000000000 --- a/docs/examples/Channel experiments.ipynb +++ /dev/null @@ -1,1360 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "User schema at /Users/jhn/qcodesrc_schema.json not found.User settings won't be validated\n" - ] - } - ], - "source": [ - "from unittest import TestCase\n", - "import unittest\n", - "\n", - "from qcodes.tests.instrument_mocks import DummyChannelInstrument, DummyChannel\n", - "from qcodes.utils.validators import Numbers\n", - "from qcodes.instrument.parameter import ManualParameter\n", - "from qcodes.loops import Loop" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "instrument = DummyChannelInstrument(name='testchanneldummy')" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Started at 2017-06-06 13:34:53\n", - "DataSet:\n", - " location = 'data/2017-06-06/#010_{name}_13-34-53'\n", - " | | | \n", - " Setpoint | p1_set | p1 | (201,)\n", - " Measured | testchanneldummy_ChanA_temperature_0_0 | testchanneldummy_ChanA_temperature | (201,)\n", - " Measured | testchanneldummy_ChanB_temperature | testchanneldummy_ChanB_temperature | (201,)\n", - " Measured | testchanneldummy_ChanC_temperature | testchanneldummy_ChanC_temperature | (201,)\n", - " Measured | testchanneldummy_ChanD_temperature | testchanneldummy_ChanD_temperature | (201,)\n", - " Measured | testchanneldummy_ChanE_temperature | testchanneldummy_ChanE_temperature | (201,)\n", - " Measured | testchanneldummy_ChanF_temperature | testchanneldummy_ChanF_temperature | (201,)\n", - " Measured | testchanneldummy_ChanA_temperature_1 | temperature | (201,)\n", - "Finished at 2017-06-06 13:34:54\n" - ] - } - ], - "source": [ - "p1 = ManualParameter(name='p1', vals=Numbers(-10, 10))\n", - "loop = Loop(p1.sweep(-10,10,0.1), 0.001).each(instrument.channels.temperature,\n", - " instrument.channels[0].temperature)\n", - "data = loop.run()\n", - "#self.assertEqual(data.p1_set.ndarray.shape, (201, ))\n", - "#self.assertEqual(data.testchanneldummy_testchanneldummy_ChanA_temperature.ndarray.shape, (201, ))\n", - "# loop = Loop(p1.sweep(-10,10,0.1), 0.001).each(instrument.channels[0].temperature)\n", - "# data = loop.run()" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "('testchanneldummy_ChanA_temperature',\n", - " 'testchanneldummy_ChanB_temperature',\n", - " 'testchanneldummy_ChanC_temperature',\n", - " 'testchanneldummy_ChanD_temperature',\n", - " 'testchanneldummy_ChanE_temperature',\n", - " 'testchanneldummy_ChanF_temperature')" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "instrument.channels.temperature.names" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'temperature'" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "instrument.A.temperature.name" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'testchanneldummy_ChanA_temperature'" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "instrument.A.temperature.full_name" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'testchanneldummy_ChanA'" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "instrument.A.temperature._instrument.name" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'testchanneldummy_ChanA'" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "instrument.channels[0].temperature._instrument.name" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'testchanneldummy'" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "instrument.channels.temperature._instrument.name" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "mcip = instrument.channels.temperature" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'testchanneldummy_Multi_temperature'" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "mcip.full_name" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "('testchanneldummy_ChanA_temperature',\n", - " 'testchanneldummy_ChanB_temperature',\n", - " 'testchanneldummy_ChanC_temperature',\n", - " 'testchanneldummy_ChanD_temperature',\n", - " 'testchanneldummy_ChanE_temperature',\n", - " 'testchanneldummy_ChanF_temperature')" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "mcip.names" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "('testchanneldummy_ChanA_temperature',\n", - " 'testchanneldummy_ChanB_temperature',\n", - " 'testchanneldummy_ChanC_temperature',\n", - " 'testchanneldummy_ChanD_temperature',\n", - " 'testchanneldummy_ChanE_temperature',\n", - " 'testchanneldummy_ChanF_temperature')" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "mcip.full_names" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "p1 = ManualParameter(name='p1', vals=Numbers(-10, 10))" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "loop = Loop(p1.sweep(-10,10,1), 1e-6).each(instrument.channels.temperature)" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "WARNING:root:negative delay -0.000000 sec\n", - "WARNING:root:negative delay -0.000000 sec\n", - "WARNING:root:negative delay -0.000000 sec\n", - "WARNING:root:negative delay -0.000001 sec\n", - "WARNING:root:negative delay -0.000001 sec\n", - "WARNING:root:negative delay -0.000001 sec\n", - "WARNING:root:negative delay -0.000001 sec\n", - "WARNING:root:negative delay -0.000001 sec\n", - "WARNING:root:negative delay -0.000000 sec\n", - "WARNING:root:negative delay -0.000000 sec\n", - "WARNING:root:negative delay -0.000000 sec\n", - "WARNING:root:negative delay -0.000001 sec\n", - "WARNING:root:negative delay -0.000001 sec\n", - "WARNING:root:negative delay -0.000001 sec\n", - "WARNING:root:negative delay -0.000001 sec\n", - "WARNING:root:negative delay -0.000001 sec\n", - "WARNING:root:negative delay -0.000001 sec\n", - "WARNING:root:negative delay -0.000001 sec\n", - "WARNING:root:negative delay -0.000002 sec\n", - "WARNING:root:negative delay -0.000002 sec\n", - "WARNING:root:negative delay -0.000000 sec\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Started at 2017-06-06 13:34:57\n", - "DataSet:\n", - " location = 'data/2017-06-06/#011_{name}_13-34-57'\n", - " | | | \n", - " Setpoint | p1_set | p1 | (21,)\n", - " Measured | testchanneldummy_ChanA_temperature | testchanneldummy_ChanA_temperature | (21,)\n", - " Measured | testchanneldummy_ChanB_temperature | testchanneldummy_ChanB_temperature | (21,)\n", - " Measured | testchanneldummy_ChanC_temperature | testchanneldummy_ChanC_temperature | (21,)\n", - " Measured | testchanneldummy_ChanD_temperature | testchanneldummy_ChanD_temperature | (21,)\n", - " Measured | testchanneldummy_ChanE_temperature | testchanneldummy_ChanE_temperature | (21,)\n", - " Measured | testchanneldummy_ChanF_temperature | testchanneldummy_ChanF_temperature | (21,)\n", - "Finished at 2017-06-06 13:34:57\n" - ] - } - ], - "source": [ - "sata =loop.run() " - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['__class__',\n", - " '__delattr__',\n", - " '__dict__',\n", - " '__dir__',\n", - " '__doc__',\n", - " '__eq__',\n", - " '__format__',\n", - " '__ge__',\n", - " '__getattr__',\n", - " '__getattribute__',\n", - " '__gt__',\n", - " '__hash__',\n", - " '__init__',\n", - " '__le__',\n", - " '__lt__',\n", - " '__module__',\n", - " '__ne__',\n", - " '__new__',\n", - " '__reduce__',\n", - " '__reduce_ex__',\n", - " '__repr__',\n", - " '__setattr__',\n", - " '__sizeof__',\n", - " '__str__',\n", - " '__subclasshook__',\n", - " '__weakref__',\n", - " '_clean_array_ids',\n", - " '_clean_param_ids',\n", - " 'action_id_map',\n", - " 'add_array',\n", - " 'add_metadata',\n", - " 'arrays',\n", - " 'background_functions',\n", - " 'complete',\n", - " 'default_formatter',\n", - " 'default_io',\n", - " 'default_parameter_array',\n", - " 'default_parameter_name',\n", - " 'delegate_attr_dicts',\n", - " 'delegate_attr_objects',\n", - " 'finalize',\n", - " 'formatter',\n", - " 'fraction_complete',\n", - " 'get_array_metadata',\n", - " 'get_changes',\n", - " 'io',\n", - " 'last_store',\n", - " 'last_write',\n", - " 'location',\n", - " 'location_provider',\n", - " 'metadata',\n", - " 'omit_delegate_attrs',\n", - " 'p1_set',\n", - " 'read',\n", - " 'read_metadata',\n", - " 'save_metadata',\n", - " 'snapshot',\n", - " 'store',\n", - " 'sync',\n", - " 'testchanneldummy_ChanA_temperature',\n", - " 'testchanneldummy_ChanB_temperature',\n", - " 'testchanneldummy_ChanC_temperature',\n", - " 'testchanneldummy_ChanD_temperature',\n", - " 'testchanneldummy_ChanE_temperature',\n", - " 'testchanneldummy_ChanF_temperature',\n", - " 'write',\n", - " 'write_copy',\n", - " 'write_period']" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "dir(sata)" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'__class__': 'qcodes.data.data_set.DataSet',\n", - " 'arrays': {'p1_set': {'__class__': 'qcodes.data.data_array.DataArray',\n", - " 'action_indices': (),\n", - " 'array_id': 'p1_set',\n", - " 'is_setpoint': True,\n", - " 'label': 'p1',\n", - " 'name': 'p1',\n", - " 'shape': (21,),\n", - " 'unit': '',\n", - " 'vals': ''},\n", - " 'testchanneldummy_ChanA_temperature': {'__class__': 'qcodes.data.data_array.DataArray',\n", - " 'action_indices': (0, 0),\n", - " 'array_id': 'testchanneldummy_ChanA_temperature',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannelInstrument',\n", - " 'instrument_name': 'testchanneldummy',\n", - " 'is_setpoint': False,\n", - " 'label': 'Temperature_A',\n", - " 'labels': ('Temperature_A',\n", - " 'Temperature_B',\n", - " 'Temperature_C',\n", - " 'Temperature_D',\n", - " 'Temperature_E',\n", - " 'Temperature_F'),\n", - " 'name': 'testchanneldummy_ChanA_temperature',\n", - " 'names': ('testchanneldummy_ChanA_temperature',\n", - " 'testchanneldummy_ChanB_temperature',\n", - " 'testchanneldummy_ChanC_temperature',\n", - " 'testchanneldummy_ChanD_temperature',\n", - " 'testchanneldummy_ChanE_temperature',\n", - " 'testchanneldummy_ChanF_temperature'),\n", - " 'setpoint_labels': None,\n", - " 'setpoint_names': None,\n", - " 'setpoint_units': None,\n", - " 'shape': (21,),\n", - " 'unit': 'K',\n", - " 'units': ('K', 'K', 'K', 'K', 'K', 'K')},\n", - " 'testchanneldummy_ChanB_temperature': {'__class__': 'qcodes.data.data_array.DataArray',\n", - " 'action_indices': (0, 1),\n", - " 'array_id': 'testchanneldummy_ChanB_temperature',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannelInstrument',\n", - " 'instrument_name': 'testchanneldummy',\n", - " 'is_setpoint': False,\n", - " 'label': 'Temperature_B',\n", - " 'labels': ('Temperature_A',\n", - " 'Temperature_B',\n", - " 'Temperature_C',\n", - " 'Temperature_D',\n", - " 'Temperature_E',\n", - " 'Temperature_F'),\n", - " 'name': 'testchanneldummy_ChanB_temperature',\n", - " 'names': ('testchanneldummy_ChanA_temperature',\n", - " 'testchanneldummy_ChanB_temperature',\n", - " 'testchanneldummy_ChanC_temperature',\n", - " 'testchanneldummy_ChanD_temperature',\n", - " 'testchanneldummy_ChanE_temperature',\n", - " 'testchanneldummy_ChanF_temperature'),\n", - " 'setpoint_labels': None,\n", - " 'setpoint_names': None,\n", - " 'setpoint_units': None,\n", - " 'shape': (21,),\n", - " 'unit': 'K',\n", - " 'units': ('K', 'K', 'K', 'K', 'K', 'K')},\n", - " 'testchanneldummy_ChanC_temperature': {'__class__': 'qcodes.data.data_array.DataArray',\n", - " 'action_indices': (0, 2),\n", - " 'array_id': 'testchanneldummy_ChanC_temperature',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannelInstrument',\n", - " 'instrument_name': 'testchanneldummy',\n", - " 'is_setpoint': False,\n", - " 'label': 'Temperature_C',\n", - " 'labels': ('Temperature_A',\n", - " 'Temperature_B',\n", - " 'Temperature_C',\n", - " 'Temperature_D',\n", - " 'Temperature_E',\n", - " 'Temperature_F'),\n", - " 'name': 'testchanneldummy_ChanC_temperature',\n", - " 'names': ('testchanneldummy_ChanA_temperature',\n", - " 'testchanneldummy_ChanB_temperature',\n", - " 'testchanneldummy_ChanC_temperature',\n", - " 'testchanneldummy_ChanD_temperature',\n", - " 'testchanneldummy_ChanE_temperature',\n", - " 'testchanneldummy_ChanF_temperature'),\n", - " 'setpoint_labels': None,\n", - " 'setpoint_names': None,\n", - " 'setpoint_units': None,\n", - " 'shape': (21,),\n", - " 'unit': 'K',\n", - " 'units': ('K', 'K', 'K', 'K', 'K', 'K')},\n", - " 'testchanneldummy_ChanD_temperature': {'__class__': 'qcodes.data.data_array.DataArray',\n", - " 'action_indices': (0, 3),\n", - " 'array_id': 'testchanneldummy_ChanD_temperature',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannelInstrument',\n", - " 'instrument_name': 'testchanneldummy',\n", - " 'is_setpoint': False,\n", - " 'label': 'Temperature_D',\n", - " 'labels': ('Temperature_A',\n", - " 'Temperature_B',\n", - " 'Temperature_C',\n", - " 'Temperature_D',\n", - " 'Temperature_E',\n", - " 'Temperature_F'),\n", - " 'name': 'testchanneldummy_ChanD_temperature',\n", - " 'names': ('testchanneldummy_ChanA_temperature',\n", - " 'testchanneldummy_ChanB_temperature',\n", - " 'testchanneldummy_ChanC_temperature',\n", - " 'testchanneldummy_ChanD_temperature',\n", - " 'testchanneldummy_ChanE_temperature',\n", - " 'testchanneldummy_ChanF_temperature'),\n", - " 'setpoint_labels': None,\n", - " 'setpoint_names': None,\n", - " 'setpoint_units': None,\n", - " 'shape': (21,),\n", - " 'unit': 'K',\n", - " 'units': ('K', 'K', 'K', 'K', 'K', 'K')},\n", - " 'testchanneldummy_ChanE_temperature': {'__class__': 'qcodes.data.data_array.DataArray',\n", - " 'action_indices': (0, 4),\n", - " 'array_id': 'testchanneldummy_ChanE_temperature',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannelInstrument',\n", - " 'instrument_name': 'testchanneldummy',\n", - " 'is_setpoint': False,\n", - " 'label': 'Temperature_E',\n", - " 'labels': ('Temperature_A',\n", - " 'Temperature_B',\n", - " 'Temperature_C',\n", - " 'Temperature_D',\n", - " 'Temperature_E',\n", - " 'Temperature_F'),\n", - " 'name': 'testchanneldummy_ChanE_temperature',\n", - " 'names': ('testchanneldummy_ChanA_temperature',\n", - " 'testchanneldummy_ChanB_temperature',\n", - " 'testchanneldummy_ChanC_temperature',\n", - " 'testchanneldummy_ChanD_temperature',\n", - " 'testchanneldummy_ChanE_temperature',\n", - " 'testchanneldummy_ChanF_temperature'),\n", - " 'setpoint_labels': None,\n", - " 'setpoint_names': None,\n", - " 'setpoint_units': None,\n", - " 'shape': (21,),\n", - " 'unit': 'K',\n", - " 'units': ('K', 'K', 'K', 'K', 'K', 'K')},\n", - " 'testchanneldummy_ChanF_temperature': {'__class__': 'qcodes.data.data_array.DataArray',\n", - " 'action_indices': (0, 5),\n", - " 'array_id': 'testchanneldummy_ChanF_temperature',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannelInstrument',\n", - " 'instrument_name': 'testchanneldummy',\n", - " 'is_setpoint': False,\n", - " 'label': 'Temperature_F',\n", - " 'labels': ('Temperature_A',\n", - " 'Temperature_B',\n", - " 'Temperature_C',\n", - " 'Temperature_D',\n", - " 'Temperature_E',\n", - " 'Temperature_F'),\n", - " 'name': 'testchanneldummy_ChanF_temperature',\n", - " 'names': ('testchanneldummy_ChanA_temperature',\n", - " 'testchanneldummy_ChanB_temperature',\n", - " 'testchanneldummy_ChanC_temperature',\n", - " 'testchanneldummy_ChanD_temperature',\n", - " 'testchanneldummy_ChanE_temperature',\n", - " 'testchanneldummy_ChanF_temperature'),\n", - " 'setpoint_labels': None,\n", - " 'setpoint_names': None,\n", - " 'setpoint_units': None,\n", - " 'shape': (21,),\n", - " 'unit': 'K',\n", - " 'units': ('K', 'K', 'K', 'K', 'K', 'K')}},\n", - " 'formatter': 'qcodes.data.gnuplot_format.GNUPlotFormat',\n", - " 'io': \"\",\n", - " 'location': 'data/2017-06-06/#011_{name}_13-34-57',\n", - " 'loop': {'__class__': 'qcodes.loops.ActiveLoop',\n", - " 'actions': [{'__class__': 'qcodes.instrument.channel.MultiChannelInstrumentParameter',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannelInstrument',\n", - " 'instrument_name': 'testchanneldummy',\n", - " 'labels': ('Temperature_A',\n", - " 'Temperature_B',\n", - " 'Temperature_C',\n", - " 'Temperature_D',\n", - " 'Temperature_E',\n", - " 'Temperature_F'),\n", - " 'name': 'Multi_temperature',\n", - " 'names': ('testchanneldummy_ChanA_temperature',\n", - " 'testchanneldummy_ChanB_temperature',\n", - " 'testchanneldummy_ChanC_temperature',\n", - " 'testchanneldummy_ChanD_temperature',\n", - " 'testchanneldummy_ChanE_temperature',\n", - " 'testchanneldummy_ChanF_temperature'),\n", - " 'setpoint_labels': None,\n", - " 'setpoint_names': None,\n", - " 'setpoint_units': None,\n", - " 'ts': None,\n", - " 'units': ('K', 'K', 'K', 'K', 'K', 'K'),\n", - " 'value': None}],\n", - " 'delay': 1e-06,\n", - " 'sweep_values': {'parameter': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", - " 'label': 'p1',\n", - " 'name': 'p1',\n", - " 'ts': None,\n", - " 'unit': '',\n", - " 'vals': '',\n", - " 'value': None},\n", - " 'values': [{'first': -10.0, 'last': 10.0, 'num': 21, 'type': 'linear'}]},\n", - " 'then_actions': [],\n", - " 'ts_end': '2017-06-06 13:34:57',\n", - " 'ts_start': '2017-06-06 13:34:57',\n", - " 'use_threads': False}}" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "sata.metadata" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'__class__': 'qcodes.tests.instrument_mocks.DummyChannelInstrument',\n", - " 'functions': {},\n", - " 'name': 'testchanneldummy',\n", - " 'parameters': {'IDN': {'__class__': 'qcodes.instrument.parameter.StandardParameter',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannelInstrument',\n", - " 'instrument_name': 'testchanneldummy',\n", - " 'label': 'IDN',\n", - " 'name': 'IDN',\n", - " 'ts': None,\n", - " 'unit': '',\n", - " 'vals': '',\n", - " 'value': None}},\n", - " 'submodules': {'A': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'functions': {},\n", - " 'name': 'testchanneldummy_ChanA',\n", - " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'instrument_name': 'testchanneldummy_ChanA',\n", - " 'label': 'Temperature_A',\n", - " 'name': 'temperature',\n", - " 'ts': '2017-06-06 13:34:53',\n", - " 'unit': 'K',\n", - " 'vals': '',\n", - " 'value': 0}},\n", - " 'submodules': {}},\n", - " 'B': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'functions': {},\n", - " 'name': 'testchanneldummy_ChanB',\n", - " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'instrument_name': 'testchanneldummy_ChanB',\n", - " 'label': 'Temperature_B',\n", - " 'name': 'temperature',\n", - " 'ts': '2017-06-06 13:34:53',\n", - " 'unit': 'K',\n", - " 'vals': '',\n", - " 'value': 0}},\n", - " 'submodules': {}},\n", - " 'C': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'functions': {},\n", - " 'name': 'testchanneldummy_ChanC',\n", - " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'instrument_name': 'testchanneldummy_ChanC',\n", - " 'label': 'Temperature_C',\n", - " 'name': 'temperature',\n", - " 'ts': '2017-06-06 13:34:53',\n", - " 'unit': 'K',\n", - " 'vals': '',\n", - " 'value': 0}},\n", - " 'submodules': {}},\n", - " 'D': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'functions': {},\n", - " 'name': 'testchanneldummy_ChanD',\n", - " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'instrument_name': 'testchanneldummy_ChanD',\n", - " 'label': 'Temperature_D',\n", - " 'name': 'temperature',\n", - " 'ts': '2017-06-06 13:34:53',\n", - " 'unit': 'K',\n", - " 'vals': '',\n", - " 'value': 0}},\n", - " 'submodules': {}},\n", - " 'E': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'functions': {},\n", - " 'name': 'testchanneldummy_ChanE',\n", - " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'instrument_name': 'testchanneldummy_ChanE',\n", - " 'label': 'Temperature_E',\n", - " 'name': 'temperature',\n", - " 'ts': '2017-06-06 13:34:53',\n", - " 'unit': 'K',\n", - " 'vals': '',\n", - " 'value': 0}},\n", - " 'submodules': {}},\n", - " 'F': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'functions': {},\n", - " 'name': 'testchanneldummy_ChanF',\n", - " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'instrument_name': 'testchanneldummy_ChanF',\n", - " 'label': 'Temperature_F',\n", - " 'name': 'temperature',\n", - " 'ts': '2017-06-06 13:34:53',\n", - " 'unit': 'K',\n", - " 'vals': '',\n", - " 'value': 0}},\n", - " 'submodules': {}},\n", - " 'channels': {'__class__': 'qcodes.instrument.channel.ChannelList',\n", - " 'snapshotable': False}}}" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "instrument.snapshot()" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'temperature': }" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "instrument.channels.parameters" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "qcodes.tests.instrument_mocks.DummyChannel" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "instrument.channels._chan_type" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'temperature': }" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "instrument.A.parameters" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "ename": "NameError", - "evalue": "name 'channels' is not defined", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0minstrument\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mchannels\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mextend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchannels\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mNameError\u001b[0m: name 'channels' is not defined" - ] - } - ], - "source": [ - "instrument.channels.extend(channels)" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - " at 0x10da5cf68>" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(DummyChannel(instrument, name, name) for name in ['g', 'h', 'i'])" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[,\n", - " ,\n", - " ]" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "list((DummyChannel(instrument, name, name) for name in ['g', 'h', 'i']))" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "instrument.channels.extend((DummyChannel(instrument, name, name) for name in ['g', 'h', 'i']))" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "ChannelList(, DummyChannel, [, , , , , , , , ])" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "instrument.channels" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'__class__': 'qcodes.instrument.channel.ChannelList', 'snapshotable': False}" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "instrument.channels.snapshot_base()" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "instrument.channels._snapshotable = True" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'__class__': 'qcodes.tests.instrument_mocks.DummyChannelInstrument',\n", - " 'functions': {},\n", - " 'name': 'testchanneldummy',\n", - " 'parameters': {'IDN': {'__class__': 'qcodes.instrument.parameter.StandardParameter',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannelInstrument',\n", - " 'instrument_name': 'testchanneldummy',\n", - " 'label': 'IDN',\n", - " 'name': 'IDN',\n", - " 'ts': None,\n", - " 'unit': '',\n", - " 'vals': '',\n", - " 'value': None}},\n", - " 'submodules': {'A': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'functions': {},\n", - " 'name': 'testchanneldummy_ChanA',\n", - " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'instrument_name': 'testchanneldummy_ChanA',\n", - " 'label': 'Temperature_A',\n", - " 'name': 'temperature',\n", - " 'ts': '2017-06-06 13:34:53',\n", - " 'unit': 'K',\n", - " 'vals': '',\n", - " 'value': 0}},\n", - " 'submodules': {}},\n", - " 'B': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'functions': {},\n", - " 'name': 'testchanneldummy_ChanB',\n", - " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'instrument_name': 'testchanneldummy_ChanB',\n", - " 'label': 'Temperature_B',\n", - " 'name': 'temperature',\n", - " 'ts': '2017-06-06 13:34:53',\n", - " 'unit': 'K',\n", - " 'vals': '',\n", - " 'value': 0}},\n", - " 'submodules': {}},\n", - " 'C': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'functions': {},\n", - " 'name': 'testchanneldummy_ChanC',\n", - " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'instrument_name': 'testchanneldummy_ChanC',\n", - " 'label': 'Temperature_C',\n", - " 'name': 'temperature',\n", - " 'ts': '2017-06-06 13:34:53',\n", - " 'unit': 'K',\n", - " 'vals': '',\n", - " 'value': 0}},\n", - " 'submodules': {}},\n", - " 'D': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'functions': {},\n", - " 'name': 'testchanneldummy_ChanD',\n", - " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'instrument_name': 'testchanneldummy_ChanD',\n", - " 'label': 'Temperature_D',\n", - " 'name': 'temperature',\n", - " 'ts': '2017-06-06 13:34:53',\n", - " 'unit': 'K',\n", - " 'vals': '',\n", - " 'value': 0}},\n", - " 'submodules': {}},\n", - " 'E': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'functions': {},\n", - " 'name': 'testchanneldummy_ChanE',\n", - " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'instrument_name': 'testchanneldummy_ChanE',\n", - " 'label': 'Temperature_E',\n", - " 'name': 'temperature',\n", - " 'ts': '2017-06-06 13:34:53',\n", - " 'unit': 'K',\n", - " 'vals': '',\n", - " 'value': 0}},\n", - " 'submodules': {}},\n", - " 'F': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'functions': {},\n", - " 'name': 'testchanneldummy_ChanF',\n", - " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'instrument_name': 'testchanneldummy_ChanF',\n", - " 'label': 'Temperature_F',\n", - " 'name': 'temperature',\n", - " 'ts': '2017-06-06 13:34:53',\n", - " 'unit': 'K',\n", - " 'vals': '',\n", - " 'value': 0}},\n", - " 'submodules': {}},\n", - " 'channels': {'__class__': 'qcodes.instrument.channel.ChannelList',\n", - " 'channels': {'testchanneldummy_ChanA': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'functions': {},\n", - " 'name': 'testchanneldummy_ChanA',\n", - " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'instrument_name': 'testchanneldummy_ChanA',\n", - " 'label': 'Temperature_A',\n", - " 'name': 'temperature',\n", - " 'ts': '2017-06-06 13:34:53',\n", - " 'unit': 'K',\n", - " 'vals': '',\n", - " 'value': 0}},\n", - " 'submodules': {}},\n", - " 'testchanneldummy_ChanB': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'functions': {},\n", - " 'name': 'testchanneldummy_ChanB',\n", - " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'instrument_name': 'testchanneldummy_ChanB',\n", - " 'label': 'Temperature_B',\n", - " 'name': 'temperature',\n", - " 'ts': '2017-06-06 13:34:53',\n", - " 'unit': 'K',\n", - " 'vals': '',\n", - " 'value': 0}},\n", - " 'submodules': {}},\n", - " 'testchanneldummy_ChanC': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'functions': {},\n", - " 'name': 'testchanneldummy_ChanC',\n", - " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'instrument_name': 'testchanneldummy_ChanC',\n", - " 'label': 'Temperature_C',\n", - " 'name': 'temperature',\n", - " 'ts': '2017-06-06 13:34:53',\n", - " 'unit': 'K',\n", - " 'vals': '',\n", - " 'value': 0}},\n", - " 'submodules': {}},\n", - " 'testchanneldummy_ChanD': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'functions': {},\n", - " 'name': 'testchanneldummy_ChanD',\n", - " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'instrument_name': 'testchanneldummy_ChanD',\n", - " 'label': 'Temperature_D',\n", - " 'name': 'temperature',\n", - " 'ts': '2017-06-06 13:34:53',\n", - " 'unit': 'K',\n", - " 'vals': '',\n", - " 'value': 0}},\n", - " 'submodules': {}},\n", - " 'testchanneldummy_ChanE': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'functions': {},\n", - " 'name': 'testchanneldummy_ChanE',\n", - " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'instrument_name': 'testchanneldummy_ChanE',\n", - " 'label': 'Temperature_E',\n", - " 'name': 'temperature',\n", - " 'ts': '2017-06-06 13:34:53',\n", - " 'unit': 'K',\n", - " 'vals': '',\n", - " 'value': 0}},\n", - " 'submodules': {}},\n", - " 'testchanneldummy_ChanF': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'functions': {},\n", - " 'name': 'testchanneldummy_ChanF',\n", - " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'instrument_name': 'testchanneldummy_ChanF',\n", - " 'label': 'Temperature_F',\n", - " 'name': 'temperature',\n", - " 'ts': '2017-06-06 13:34:53',\n", - " 'unit': 'K',\n", - " 'vals': '',\n", - " 'value': 0}},\n", - " 'submodules': {}},\n", - " 'testchanneldummy_g': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'functions': {},\n", - " 'name': 'testchanneldummy_g',\n", - " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'instrument_name': 'testchanneldummy_g',\n", - " 'label': 'Temperature_g',\n", - " 'name': 'temperature',\n", - " 'ts': '2017-06-06 13:34:59',\n", - " 'unit': 'K',\n", - " 'vals': '',\n", - " 'value': 0}},\n", - " 'submodules': {}},\n", - " 'testchanneldummy_h': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'functions': {},\n", - " 'name': 'testchanneldummy_h',\n", - " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'instrument_name': 'testchanneldummy_h',\n", - " 'label': 'Temperature_h',\n", - " 'name': 'temperature',\n", - " 'ts': '2017-06-06 13:34:59',\n", - " 'unit': 'K',\n", - " 'vals': '',\n", - " 'value': 0}},\n", - " 'submodules': {}},\n", - " 'testchanneldummy_i': {'__class__': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'functions': {},\n", - " 'name': 'testchanneldummy_i',\n", - " 'parameters': {'temperature': {'__class__': 'qcodes.instrument.parameter.ManualParameter',\n", - " 'instrument': 'qcodes.tests.instrument_mocks.DummyChannel',\n", - " 'instrument_name': 'testchanneldummy_i',\n", - " 'label': 'Temperature_i',\n", - " 'name': 'temperature',\n", - " 'ts': '2017-06-06 13:34:59',\n", - " 'unit': 'K',\n", - " 'vals': '',\n", - " 'value': 0}},\n", - " 'submodules': {}}},\n", - " 'snapshotable': True}}}" - ] - }, - "execution_count": 30, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "instrument.snapshot()" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'IDN': }" - ] - }, - "execution_count": 31, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "instrument.parameters" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'temperature': }" - ] - }, - "execution_count": 32, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "instrument.channels.parameters" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'temperature': }" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "instrument.A.parameters" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'Temperature_A'" - ] - }, - "execution_count": 36, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "instrument.channels.parameters['temperature']" - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 37, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "'temperature' in instrument.channels.parameters" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1" - ] - }, - "execution_count": 38, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "len(instrument.channels.parameters)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.5.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/examples/Channel instrument with array parameter.ipynb b/docs/examples/Channel instrument with array parameter.ipynb deleted file mode 100644 index b9c64d17694b..000000000000 --- a/docs/examples/Channel instrument with array parameter.ipynb +++ /dev/null @@ -1,541 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "User schema at /Users/jhn/qcodesrc_schema.json not found.User settings won't be validated\n" - ] - } - ], - "source": [ - "from qcodes import InstrumentChannel, Instrument, ChannelList\n", - "from qcodes import ManualParameter, MultiParameter, ArrayParameter\n", - "from qcodes.utils.validators import Numbers\n", - "import numpy as np" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "class DummyChannel(InstrumentChannel):\n", - " \"\"\"\n", - " A single dummy channel implementation\n", - " \"\"\"\n", - "\n", - " def __init__(self, parent, name, channel):\n", - " super().__init__(parent, name)\n", - "\n", - " self._channel = channel\n", - "\n", - " # Add the various channel parameters\n", - " self.add_parameter('temperature',\n", - " parameter_class=ManualParameter,\n", - " initial_value=0,\n", - " label=\"Temperature_{}\".format(channel),\n", - " unit='K',\n", - " vals=Numbers(0, 300))\n", - " \n", - " self.add_parameter(name='foobar',\n", - " parameter_class=MultiSetPointParam)\n", - " \n", - " self.add_parameter(name='myarray',\n", - " parameter_class=ArraySetPointParam)\n", - "\n", - "\n", - "class DummyChannelInstrument(Instrument):\n", - " \"\"\"\n", - " Dummy instrument with channels\n", - " \"\"\"\n", - "\n", - " def __init__(self, name, **kwargs):\n", - " super().__init__(name, **kwargs)\n", - "\n", - " channels = ChannelList(self, \"TempSensors\", DummyChannel, snapshotable=False)\n", - " for chan_name in ('A', 'B', 'C', 'D', 'E', 'F'):\n", - " channel = DummyChannel(self, 'Chan{}'.format(chan_name), chan_name)\n", - " channels.append(channel)\n", - " self.add_submodule(chan_name, channel)\n", - " self.add_submodule(\"channels\", channels)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "class MultiSetPointParam(MultiParameter):\n", - " \"\"\"\n", - " Multiparameter which only purpose it to test that units, setpoints\n", - " and so on are copied correctly to the individual arrays in the datarray.\n", - " \"\"\"\n", - " def __init__(self, instrument, name):\n", - " name = 'testparameter'\n", - " shapes = ((5,), (5,))\n", - " names = ('this', 'that')\n", - " labels = ('this label', 'that label')\n", - " units = ('this unit', 'that unit')\n", - " sp_base = tuple(np.linspace(5, 9, 5))\n", - " setpoints = ((sp_base,), (sp_base,))\n", - " setpoint_names = (('this_setpoint',), ('this_setpoint',))\n", - " setpoint_labels = (('this setpoint',), ('this setpoint',))\n", - " setpoint_units = (('this setpointunit',), ('this setpointunit',))\n", - " super().__init__(name, names, shapes,\n", - " labels=labels,\n", - " units=units,\n", - " setpoints=setpoints,\n", - " setpoint_labels=setpoint_labels,\n", - " setpoint_names=setpoint_names,\n", - " setpoint_units=setpoint_units)\n", - "\n", - " def get(self):\n", - " return np.zeros(5), np.ones(5)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "class ArraySetPointParam(ArrayParameter):\n", - " \"\"\"\n", - " Multiparameter which only purpose it to test that units, setpoints\n", - " and so on are copied correctly to the individual arrays in the datarray.\n", - " \"\"\"\n", - " def __init__(self, instrument, name):\n", - " name = 'testparameter'\n", - " shape = (5,)\n", - " label = 'this label'\n", - " unit = 'this unit'\n", - " sp_base = tuple(np.linspace(5, 9, 5))\n", - " setpoints = (sp_base,)\n", - " setpoint_names = ('this_setpoint',)\n", - " setpoint_labels = ('this setpoint',)\n", - " setpoint_units = ('this setpointunit',)\n", - " super().__init__(name,\n", - " shape,\n", - " label=label,\n", - " unit=unit,\n", - " setpoints=setpoints,\n", - " setpoint_labels=setpoint_labels,\n", - " setpoint_names=setpoint_names,\n", - " setpoint_units=setpoint_units)\n", - "\n", - " def get(self):\n", - " return np.random.rand(5)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "inst = DummyChannelInstrument('foobar')" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "inst.channels[0]" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "from qcodes import Loop" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Started at 2017-06-06 17:11:20\n", - "DataSet:\n", - " location = 'data/2017-06-06/#020_{name}_17-11-20'\n", - " | | | \n", - " Setpoint | foobar_ChanA_temperature_set | temperature | (11,)\n", - " Setpoint | this_setpoint_set | this_setpoint | (11, 5)\n", - " Measured | this | this | (11, 5)\n", - " Measured | that | that | (11, 5)\n", - "Finished at 2017-06-06 17:11:31\n" - ] - } - ], - "source": [ - "data1 = Loop(inst.A.temperature.sweep(0,10,1), 1).each(inst.A.foobar).run()" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "DataArray[11,5]: this\n", - "array([[ 0., 0., 0., 0., 0.],\n", - " [ 0., 0., 0., 0., 0.],\n", - " [ 0., 0., 0., 0., 0.],\n", - " [ 0., 0., 0., 0., 0.],\n", - " [ 0., 0., 0., 0., 0.],\n", - " [ 0., 0., 0., 0., 0.],\n", - " [ 0., 0., 0., 0., 0.],\n", - " [ 0., 0., 0., 0., 0.],\n", - " [ 0., 0., 0., 0., 0.],\n", - " [ 0., 0., 0., 0., 0.],\n", - " [ 0., 0., 0., 0., 0.]])" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "data1.this" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Started at 2017-06-06 17:11:33\n", - "DataSet:\n", - " location = 'data/2017-06-06/#021_{name}_17-11-33'\n", - " | | | \n", - " Setpoint | foobar_ChanA_temperature_set | temperature | (11,)\n", - " Setpoint | this_setpoint_set | this_setpoint | (11, 5)\n", - " Measured | this | this | (11, 5)\n", - " Measured | that | that | (11, 5)\n", - "Finished at 2017-06-06 17:11:44\n" - ] - } - ], - "source": [ - "data2 = Loop(inst.A.temperature.sweep(0,10,1), 1).each(inst.channels[0].foobar).run()" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "ename": "AttributeError", - "evalue": "'MultiSetPointParam' object has no attribute 'label'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mdata2\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mLoop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minst\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mA\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtemperature\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msweep\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0meach\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minst\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mchannels\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfoobar\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m~/src/Qcodes/qcodes/instrument/channel.py\u001b[0m in \u001b[0;36m__getattr__\u001b[0;34m(self, name)\u001b[0m\n\u001b[1;32m 299\u001b[0m \u001b[0mnames\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"{}_{}\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchan\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 300\u001b[0m \u001b[0mshapes\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# TODO: Pull shapes intelligently\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 301\u001b[0;31m \u001b[0mlabels\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchan\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mparameters\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlabel\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 302\u001b[0m \u001b[0munits\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchan\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mparameters\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0munit\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 303\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/src/Qcodes/qcodes/instrument/channel.py\u001b[0m in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 299\u001b[0m \u001b[0mnames\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"{}_{}\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchan\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 300\u001b[0m \u001b[0mshapes\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# TODO: Pull shapes intelligently\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 301\u001b[0;31m \u001b[0mlabels\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchan\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mparameters\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlabel\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 302\u001b[0m \u001b[0munits\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchan\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mparameters\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0munit\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 303\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mAttributeError\u001b[0m: 'MultiSetPointParam' object has no attribute 'label'" - ] - } - ], - "source": [ - "data2 = Loop(inst.A.temperature.sweep(0,10,1), 1).each(inst.channels[0:1].foobar).run()" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Started at 2017-06-06 17:13:16\n", - "DataSet:\n", - " location = 'data/2017-06-06/#023_{name}_17-13-16'\n", - " | | | \n", - " Setpoint | foobar_ChanA_temperature_set | temperature | (11,)\n", - " Measured | testparameter | testparameter | (11, 5)\n", - "Finished at 2017-06-06 17:13:27\n" - ] - } - ], - "source": [ - "data3 = Loop(inst.A.temperature.sweep(0,10,1), 1).each(inst.A.myarray).run()" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Started at 2017-06-06 17:14:09\n", - "DataSet:\n", - " location = 'data/2017-06-06/#024_{name}_17-14-09'\n", - " | | | \n", - " Setpoint | foobar_ChanA_temperature_set | temperature | (11,)\n", - " Measured | testparameter | testparameter | (11, 5)\n", - "Finished at 2017-06-06 17:14:20\n" - ] - } - ], - "source": [ - "data3 = Loop(inst.A.temperature.sweep(0,10,1), 1).each(inst.channels[0].myarray).run()" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Started at 2017-06-06 17:14:32\n", - "DataSet:\n", - " location = 'data/2017-06-06/#025_{name}_17-14-32'\n", - " | | | \n", - " Setpoint | foobar_ChanA_temperature_set | temperature | (11,)\n", - " Measured | foobar_ChanA_myarray | foobar_ChanA_myarray | (11,)\n", - "Finished at 2017-06-06 17:14:33\n" - ] - }, - { - "ename": "ValueError", - "evalue": "setting an array element with a sequence.", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mdata3\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mLoop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minst\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mA\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtemperature\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msweep\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0meach\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minst\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mchannels\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmyarray\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m~/src/Qcodes/qcodes/loops.py\u001b[0m in \u001b[0;36mrun\u001b[0;34m(self, use_threads, quiet, station, progress_interval, *args, **kwargs)\u001b[0m\n\u001b[1;32m 717\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mquiet\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 718\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdatetime\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnow\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstrftime\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Started at %Y-%m-%d %H:%M:%S'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 719\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_run_wrapper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 720\u001b[0m \u001b[0mds\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata_set\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 721\u001b[0m \u001b[0;32mfinally\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/src/Qcodes/qcodes/loops.py\u001b[0m in \u001b[0;36m_run_wrapper\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 764\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_run_wrapper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 765\u001b[0m \u001b[0;31m# try:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 766\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_run_loop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 767\u001b[0m \u001b[0;31m# finally:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 768\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mhasattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'data_set'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/src/Qcodes/qcodes/loops.py\u001b[0m in \u001b[0;36m_run_loop\u001b[0;34m(self, first_delay, action_indices, loop_indices, current_values, **ignore_kwargs)\u001b[0m\n\u001b[1;32m 836\u001b[0m f(first_delay=delay,\n\u001b[1;32m 837\u001b[0m \u001b[0mloop_indices\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mnew_indices\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 838\u001b[0;31m current_values=new_values)\n\u001b[0m\u001b[1;32m 839\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 840\u001b[0m \u001b[0;31m# after the first action, no delay is inherited\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/src/Qcodes/qcodes/actions.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, loop_indices, **ignore_kwargs)\u001b[0m\n\u001b[1;32m 144\u001b[0m \u001b[0mout_dict\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mparam_id\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mparam_out\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 145\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 146\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstore\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mloop_indices\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mout_dict\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 147\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 148\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/src/Qcodes/qcodes/data/data_set.py\u001b[0m in \u001b[0;36mstore\u001b[0;34m(self, loop_indices, ids_values)\u001b[0m\n\u001b[1;32m 384\u001b[0m \"\"\"\n\u001b[1;32m 385\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0marray_id\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalue\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mids_values\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mitems\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 386\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marrays\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0marray_id\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mloop_indices\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 387\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlast_store\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtime\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtime\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 388\u001b[0m if (self.write_period is not None and\n", - "\u001b[0;32m~/src/Qcodes/qcodes/data/data_array.py\u001b[0m in \u001b[0;36m__setitem__\u001b[0;34m(self, loop_indices, value)\u001b[0m\n\u001b[1;32m 339\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_update_modified_range\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmin_li\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmax_li\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 340\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 341\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mndarray\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__setitem__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mloop_indices\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 342\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 343\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__getitem__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mloop_indices\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mValueError\u001b[0m: setting an array element with a sequence." - ] - } - ], - "source": [ - "data3 = Loop(inst.A.temperature.sweep(0,10,1), 1).each(inst.channels[0:1].myarray).run()" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(array([ 0.16087586, 0.63407137, 0.40978536, 0.76905476, 0.82876632]),\n", - " array([ 0.69882021, 0.73425924, 0.25545122, 0.86773728, 0.10338811]),\n", - " array([ 0.8698955 , 0.3354928 , 0.07489469, 0.38043035, 0.70433896]),\n", - " array([ 0.45961196, 0.92813073, 0.5977839 , 0.35455607, 0.06801803]),\n", - " array([ 0.49382892, 0.90160562, 0.36905395, 0.33856894, 0.39967096]),\n", - " array([ 0.8050116 , 0.74691103, 0.65995307, 0.85211923, 0.57228292]))" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "inst.channels.myarray.get()" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "ename": "AttributeError", - "evalue": "'MultiSetPointParam' object has no attribute 'label'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0minst\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mchannels\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfoobar\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m~/src/Qcodes/qcodes/instrument/channel.py\u001b[0m in \u001b[0;36m__getattr__\u001b[0;34m(self, name)\u001b[0m\n\u001b[1;32m 299\u001b[0m \u001b[0mnames\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"{}_{}\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchan\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 300\u001b[0m \u001b[0mshapes\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# TODO: Pull shapes intelligently\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 301\u001b[0;31m \u001b[0mlabels\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchan\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mparameters\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlabel\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 302\u001b[0m \u001b[0munits\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchan\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mparameters\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0munit\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 303\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/src/Qcodes/qcodes/instrument/channel.py\u001b[0m in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 299\u001b[0m \u001b[0mnames\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"{}_{}\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchan\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 300\u001b[0m \u001b[0mshapes\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# TODO: Pull shapes intelligently\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 301\u001b[0;31m \u001b[0mlabels\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchan\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mparameters\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlabel\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 302\u001b[0m \u001b[0munits\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchan\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mparameters\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0munit\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchan\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_channels\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 303\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mAttributeError\u001b[0m: 'MultiSetPointParam' object has no attribute 'label'" - ] - } - ], - "source": [ - "inst.channels.foobar.get()" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "('this label',\n", - " 'this label',\n", - " 'this label',\n", - " 'this label',\n", - " 'this label',\n", - " 'this label')" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "inst.channels.myarray.labels" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'this label'" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "inst.A.myarray.label" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(5,)" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "inst.A.myarray.shape" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "((), (), (), (), (), ())" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "inst.channels.myarray.shapes" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.5.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} From f05a9708a23ea6001374f925fc511252c5135c10 Mon Sep 17 00:00:00 2001 From: WilliamHPNielsen Date: Thu, 8 Jun 2017 16:09:34 +0200 Subject: [PATCH 39/49] feat: Add channelised QDac driver Add a QDac driver using the new channelisation feature --- .../instrument_drivers/QDev/QDac_channels.py | 654 ++++++++++++++++++ 1 file changed, 654 insertions(+) create mode 100644 qcodes/instrument_drivers/QDev/QDac_channels.py diff --git a/qcodes/instrument_drivers/QDev/QDac_channels.py b/qcodes/instrument_drivers/QDev/QDac_channels.py new file mode 100644 index 000000000000..0b361b86814f --- /dev/null +++ b/qcodes/instrument_drivers/QDev/QDac_channels.py @@ -0,0 +1,654 @@ +# QCoDeS driver for QDac using channels + +import time +import visa +import logging +import numpy as np + +from datetime import datetime +from functools import partial +from operator import xor +from collections import OrderedDict + +from qcodes.instrument.channel import InstrumentChannel, ChannelList +from qcodes.instrument.parameter import ManualParameter +from qcodes.instrument.visa import VisaInstrument +from qcodes.utils import validators as vals + +log = logging.getLogger(__name__) + + +class QDacChannel(InstrumentChannel): + """ + A single output channel of the QDac. + + Exposes chan.v, chan.vrange, chan.slope, chan.i, chan.irange + """ + + _CHANNEL_VALIDATION = vals.Numbers(1, 48) + + def __init__(self, parent, name, channum): + """ + Args: + parent (Instrument): The instrument to which the channel is + attached. + name (str): The name of the channel + channum (int): The number of the channel in question (1-48) + """ + super().__init__(parent, name) + + # Validate the channel + self._CHANNEL_VALIDATION.validate(channum) + + # Add the parameters + + self.add_parameter('v', + label='Channel {} voltage'.format(channum), + unit='V', + set_cmd=partial(self._parent._set_voltage, channum), + get_cmd=partial(self._parent.read_state, channum, 'v'), + get_parser=float, + vals=vals.Numbers(-10, 10) # TODO: update onthefly + ) + + self.add_parameter('vrange', + label='Channel {} atten.'.format(channum), + set_cmd=partial(self._parent._set_vrange, channum), + get_cmd=partial(self._parent.read_state, channum, + 'vrange'), + vals=vals.Enum(0, 1) + ) + + self.add_parameter('i', + label='Channel {} current'.format(channum), + get_cmd='get {}'.format(channum), + unit='A', + get_parser=self._parent._current_parser + ) + + self.add_parameter('irange', + label='Channel {} irange'.format(channum), + set_cmd='cur {} {}'.format(channum, '{}'), + get_cmd='cur {}'.format(channum, '{}'), + get_parser=int + ) + + self.add_parameter('slope', + label='Channel {} slope'.format(channum), + unit='V/s', + set_cmd=partial(self._parent._setslope, channum), + get_cmd=partial(self._parent._getslope, channum), + vals=vals.MultiType(vals.Enum('Inf'), + vals.Numbers(1e-3, 100)) + ) + + self.add_parameter('sync', + label='Channel {} sync output'.format(channum), + set_cmd=partial(self._parent._setsync, channum), + get_cmd=partial(self._parent._getsync, channum), + vals=vals.Ints(0, 5) + ) + + self.add_parameter(name='sync_delay'.format(channum), + label='Channel {} sync pulse delay'.format(channum), + unit='s', + parameter_class=ManualParameter, + initial_value=0 + ) + + self.add_parameter(name='sync_duration', + label='Channel {} sync pulse duration'.format(channum), + unit='s', + parameter_class=ManualParameter, + initial_value=0.01 + ) + + +class QDac(VisaInstrument): + """ + Channelised driver for the QDev digital-analog converter QDac + + Based on "DAC_commands_v_13.pdf" + Tested with Software Version: 0.170202 + + The driver assumes that the instrument is ALWAYS in verbose mode OFF + """ + + voltage_range_status = {'X 1': 10, 'X 0.1': 1} + + # set nonzero value (seconds) to accept older status when reading settings + max_status_age = 1 + + def __init__(self, name, address, num_chans=48, update_currents=True): + """ + Instantiates the instrument. + + Args: + name (str): The instrument name used by qcodes + address (str): The VISA name of the resource + num_chans (int): Number of channels to assign. Default: 48 + update_currents (bool): Whether to query all channels for their + current current value on startup. Default: True. + + Returns: + QDac object + """ + super().__init__(name, address) + handle = self.visa_handle + + # This is the baud rate on power-up. It can be changed later but + # you must start out with this value. + handle.baud_rate = 480600 + handle.parity = visa.constants.Parity(0) + handle.data_bits = 8 + self.set_terminator('\n') + # TODO: do we want a method for write termination too? + handle.write_termination = '\n' + # TODO: do we need a query delay for robust operation? + self._write_response = '' + + # The following bool is used in self.write + self.debugmode = False + + if self._get_firmware_version() < 0.170202: + raise RuntimeError(''' + Obsolete QDAC Software version detected. + QCoDeS only supports version 0.170202 or newer. + Contact rikke.lutge@nbi.ku.dk for an update. + ''') + + self.num_chans = num_chans + + # Assigned slopes. Entries will eventually be [chan, slope] + self._slopes = [] + # Function generators (used in _set_voltage) + self._fgs = set(range(1, 9)) + self._assigned_fgs = {} # {chan: fg} + # Sync channels + self._syncoutputs = [] # Entries: [chan, syncchannel] + + self.chan_range = range(1, 1 + self.num_chans) + self.channel_validator = vals.Ints(1, self.num_chans) + + channels = ChannelList(self, "Channels", QDacChannel, + snapshotable=False) + + for i in self.chan_range: + channel = QDacChannel(self, 'chan{}'.format(i), i) + channels.append(channel) + # Should raise valueerror if name is invalid (silently fails now) + self.add_submodule('ch{:02}'.format(i), channel) + channels.lock() + self.add_submodule('channels', channels) + + for board in range(6): + for sensor in range(3): + label = 'Board {}, Temperature {}'.format(board, sensor) + self.add_parameter(name='temp{}_{}'.format(board, sensor), + label=label, + unit='C', + get_cmd='tem {} {}'.format(board, sensor), + get_parser=self._num_verbose) + + self.add_parameter(name='cal', + set_cmd='cal {}', + vals=self.channel_validator) + # TO-DO: maybe it's too dangerous to have this settable. + # And perhaps ON is a better verbose mode default? + self.add_parameter(name='verbose', + set_cmd='ver {}', + val_mapping={True: 1, False: 0}) + + # Initialise the instrument, all channels DC (unbind func. generators) + for chan in self.chan_range: + # Note: this call does NOT change the voltage on the channel + self.write('wav {} 0 1 0'.format(chan)) + + self.verbose.set(False) + self.connect_message() + log.info('[*] Querying all channels for voltages and currents...') + self._get_status(readcurrents=update_currents) + log.info('[+] Done') + + ######################### + # Channel gets/sets + ######################### + + def _set_voltage(self, chan, v_set): + """ + set_cmd for the chXX_v parameter + + Args: + chan (int): The 1-indexed channel number + v_set (float): The target voltage + + If a finite slope has been assigned, we assign a function generator to + ramp the voltage. + """ + # validation + atten = self.channels[chan-1].vrange.get_latest() + + attendict = {0: 10, 1: 1, 10: 10} + if abs(v_set) > attendict[atten]: + v_set = np.sign(v_set)*attendict[atten] + log.warning('Requested voltage outside reachable range.' + + ' Setting voltage on channel ' + + '{} to {} V'.format(chan, v_set)) + + slopechans = [sl[0] for sl in self._slopes] + if chan in slopechans: + slope = [sl[1] for sl in self._slopes if sl[0] == chan][0] + # find and assign fg + fg = min(self._fgs.difference(set(self._assigned_fgs.values()))) + self._assigned_fgs[chan] = fg + # We need .get and not get_latest in case a ramp was interrupted + v_start = self.channels[chan-1].v.get() + time = abs(v_set-v_start)/slope + log.info('Slope: {}, time: {}'.format(slope, time)) + # Attenuation compensation and syncing + # happen inside _rampvoltage + self._rampvoltage(chan, fg, v_start, v_set, time) + else: + # compensate for the 0.1 multiplier, if it's on + if self.channels[chan-1].vrange.get_latest() == 1: + v_set = v_set*10 + # set the mode back to DC in case it had been changed + self.write('wav {} 0 0 0'.format(chan)) + self.write('set {} {:.6f}'.format(chan, v_set)) + + def _set_vrange(self, chan, switchint): + """ + set_cmd for the chXX_vrange parameter + + The switchint is an integer. 1 means attenuation ON. + + Since the vrange is actually a 20 dB attenuator (amplitude factor 0.1) + immediately applied to the channel output, we must update the voltage + parameter accordingly + """ + + tdict = {'-10 V to 10 V': 0, + '-1 V to 1 V': 1, + 10: 0, + 0: 0, + 1: 1} + + old = tdict[self.channels[chan-1].vrange.get_latest()] + + self.write('vol {} {}'.format(chan, switchint)) + + if xor(old, switchint): + voltageparam = self.channels[chan-1].v + oldvoltage = voltageparam.get_latest() + newvoltage = {0: 10, 1: 0.1}[switchint]*oldvoltage + voltageparam._save_val(newvoltage) + + def _num_verbose(self, s): + """ + turn a return value from the QDac into a number. + If the QDac is in verbose mode, this involves stripping off the + value descriptor. + """ + if self.verbose.get_latest(): + s = s.split[': '][-1] + return float(s) + + def _current_parser(self, s): + """ + parser for chXX_i parameter + """ + return 1e-6*self._num_verbose(s) + + def read_state(self, chan, param): + """ + specific routine for reading items out of status response + + Args: + chan (int): The 1-indexed channel number + param (str): The parameter in question, e.g. 'v' or 'vrange' + """ + if chan not in self.chan_range: + raise ValueError('valid channels are {}'.format(self.chan_range)) + valid_params = ('v', 'vrange', 'irange') + if param not in valid_params: + raise ValueError( + 'read_state valid params are {}'.format(valid_params)) + + self._get_status(readcurrents=False) + + parameter = 'ch{:02}_{}'.format(chan, param) + value = getattr(self.channels[chan-1], param).get_latest() + + returnmap = {'vrange': {1: 1, 10: 0}, + 'irange': {0: '1 muA', 1: '100 muA'}} + + if 'range' in param: + value = returnmap[param][value] + + return value + + def _get_status(self, readcurrents=False): + r''' + Function to query the instrument and get the status of all channels. + Takes a while to finish. + + The `status` call generates 51 lines of output. Send the command and + read the first one, which is the software version line + the full output looks like: + Software Version: 0.160218\r\n + Channel\tOut V\t\tVoltage range\tCurrent range\n + \n + 8\t 0.000000\t\tX 1\t\tpA\n + 7\t 0.000000\t\tX 1\t\tpA\n + ... (all 48 channels like this in a somewhat peculiar order) + (no termination afterward besides the \n ending the last channel) + returns a list of dicts [{v, vrange, irange}] + NOTE - channels are 1-based, but the return is a list, so of course + 0-based, ie chan1 is out[0] + ''' + + # Status call + + version_line = self.ask('status') + + if version_line.startswith('Software Version: '): + self.version = version_line.strip().split(': ')[1] + else: + self._wait_and_clear() + raise ValueError('unrecognized version line: ' + version_line) + + header_line = self.read() + headers = header_line.lower().strip('\r\n').split('\t') + expected_headers = ['channel', 'out v', '', 'voltage range', + 'current range'] + if headers != expected_headers: + raise ValueError('unrecognized header line: ' + header_line) + + chans = [{} for i in self.chan_range] + chans_left = set(self.chan_range) + while chans_left: + line = self.read().strip() + if not line: + continue + chanstr, v, _, vrange, _, irange = line.split('\t') + chan = int(chanstr) + chanstr = '{:02}'.format(chan) + + irange_trans = {'hi cur': 1, 'lo cur': 0} + + # The following dict must be ordered to ensure that vrange comes + # before v when iterating through it + vals_dict = OrderedDict() + vals_dict.update({'vrange': ('vrange', + self.voltage_range_status[vrange.strip()])}) + vals_dict.update({'irange': ('irange', irange_trans[irange])}) + vals_dict.update({'v': ('v', float(v))}) + + chans[chan - 1] = vals_dict + for param in vals_dict: + value = vals_dict[param][1] + if param == 'vrange': + attenuation = 0.1*value + if param == 'v': + value *= attenuation + getattr(self.channels[chan-1], param)._save_val(value) + chans_left.remove(chan) + + if readcurrents: + for chan in range(1, self.num_chans+1): + param = self.channels[chan-1].i + param._save_val(param.get()) + + self._status = chans + self._status_ts = datetime.now() + return chans + + def _setsync(self, chan, sync): + """ + set_cmd for the chXX_sync parameter. + + Args: + chan (int): The channel number (1-48) + sync (int): The associated sync output. 0 means 'unassign' + """ + + if chan not in range(1, 49): + raise ValueError('Channel number must be 1-48.') + + if sync == 0: + # try to remove the sync from internal bookkeeping + try: + sc = self._syncoutputs + to_remove = [sc.index(syn) for syn in sc if syn[0] == chan][0] + self._syncoutputs.remove(sc[to_remove]) + except IndexError: + pass + # free the previously assigned sync + oldsync = self.channels[chan-1].sync.get_latest() + if oldsync is not None: + self.write('syn {} 0 0 0'.format(oldsync)) + return + + if sync in [syn[1] for syn in self._syncoutputs]: + oldchan = [syn[0] for syn in self._syncoutputs if syn[1] == sync][0] + self._syncoutputs.remove([oldchan, sync]) + + if chan in [syn[0] for syn in self._syncoutputs]: + oldsyn = [syn[1] for syn in self._syncoutputs if syn[0] == chan][0] + self._syncoutputs[self._syncoutputs.index([chan, oldsyn])] = [chan, + sync] + return + + self._syncoutputs.append([chan, sync]) + return + + def _getsync(self, chan): + """ + get_cmd of the chXX_sync parameter + """ + if chan in [syn[0] for syn in self._syncoutputs]: + sync = [syn[1] for syn in self._syncoutputs if syn[0] == chan][0] + return sync + else: + return 0 + + def _setslope(self, chan, slope): + """ + set_cmd for the chXX_slope parameter, the maximum slope of a channel. + + Args: + chan (int): The channel number (1-48) + slope (Union[float, str]): The slope in V/s. Write 'Inf' to allow + arbitraryly small rise times. + """ + if chan not in range(1, 49): + raise ValueError('Channel number must be 1-48.') + + if slope == 'Inf': + self.write('wav {} 0 0 0'.format(chan)) + + # Now clear the assigned slope and function generator (if possible) + try: + self._assigned_fgs.pop(chan) + except KeyError: + pass + # Remove a sync output, if one was assigned + syncchans = [syn[0] for syn in self._syncoutputs] + if chan in syncchans: + self.channels[chan-1].sync.set(0) + try: + sls = self._slopes + to_remove = [sls.index(sl) for sl in sls if sl[0] == chan][0] + self._slopes.remove(sls[to_remove]) + return + # If the value was already 'Inf', the channel was not + # in the list and nothing happens + except IndexError: + return + + if chan in [sl[0] for sl in self._slopes]: + oldslope = [sl[1] for sl in self._slopes if sl[0] == chan][0] + self._slopes[self._slopes.index([chan, oldslope])] = [chan, slope] + return + + if len(self._slopes) >= 8: + rampchans = ', '.join([str(c[0]) for c in self._slopes]) + raise ValueError('Can not assign finite slope to more than ' + + "8 channels. Assign 'Inf' to at least one of " + + 'the following channels: {}'.format(rampchans)) + + self._slopes.append([chan, slope]) + return + + def _getslope(self, chan): + """ + get_cmd of the chXX_slope parameter + """ + if chan in [sl[0] for sl in self._slopes]: + slope = [sl[1] for sl in self._slopes if sl[0] == chan][0] + return slope + else: + return 'Inf' + + def printslopes(self): + """ + Print the finite slopes assigned to channels + """ + for sl in self._slopes: + print('Channel {}, slope: {} (V/s)'.format(sl[0], sl[1])) + + def _rampvoltage(self, chan, fg, v_start, setvoltage, ramptime): + """ + Smoothly ramp the voltage of a channel by the means of a function + generator. Helper function used by _set_voltage. + + Args: + chan (int): The channel number (counting from 1) + fg (int): The function generator (counting from 1) + setvoltage (float): The voltage to ramp to + ramptime (float): The ramp time in seconds. + """ + + # Crazy stuff happens if the period is too small, e.g. the channel + # can jump to its max voltage + if ramptime <= 0.002: + ramptime = 0 + log.warning('Cancelled a ramp with a ramptime of ' + '{} s'.format(ramptime) + '. Voltage not changed.') + + offset = v_start + amplitude = setvoltage-v_start + if self.channels[chan-1].vrange.get_latest() == 1: + offset *= 10 + amplitude *= 10 + + chanmssg = 'wav {} {} {} {}'.format(chan, fg, + amplitude, + offset) + + if chan in [syn[0] for syn in self._syncoutputs]: + #syncing = True + sync = [syn[1] for syn in self._syncoutputs if syn[0] == chan][0] + sync_duration = 1000*self.channels[chan-1].sync_duration.get() + sync_delay = 1000*self.channels[chan-1].sync_delay.get() + self.write('syn {} {} {} {}'.format(sync, fg, + sync_delay, + sync_duration)) + else: + syncing = False + + typedict = {'SINE': 1, 'SQUARE': 2, 'RAMP': 3} + + typeval = typedict['RAMP'] + dutyval = 100 + # s -> ms + periodval = ramptime*1e3 + repval = 1 + funmssg = 'fun {} {} {} {} {}'.format(fg, + typeval, periodval, + dutyval, repval) + self.write(chanmssg) + self.write(funmssg) + + def write(self, cmd): + """ + QDac always returns something even from set commands, even when + verbose mode is off, so we'll override write to take this out + if you want to use this response, we put it in self._write_response + (but only for the very last write call) + + Note that this procedure makes it cumbersome to handle the returned + messages from concatenated commands, e.g. 'wav 1 1 1 0;fun 2 1 100 1 1' + Please don't use concatenated commands + + TODO (WilliamHPNielsen): add automatic de-concatenation of commands. + """ + if self.debugmode: + log.info('Sending command string: {}'.format(cmd)) + + nr_bytes_written, ret_code = self.visa_handle.write(cmd) + self.check_error(ret_code) + self._write_response = self.visa_handle.read() + + def read(self): + return self.visa_handle.read() + + def _wait_and_clear(self, delay=0.5): + time.sleep(delay) + self.visa_handle.clear() + + def connect_message(self): + """ + Override of the standard Instrument class connect_message. + Usually, the response to `*IDN?` is printed. Here, the + software version is printed. + """ + self.visa_handle.write('status') + + log.info('Connected to QDac on {}, {}'.format(self._address, + self.visa_handle.read())) + + # take care of the rest of the output + for ii in range(50): + self.visa_handle.read() + + def _get_firmware_version(self): + self.write('status') + FW_str = self._write_response + FW_version = float(FW_str.replace('Software Version: ', '')) + for ii in range(50): + self.read() + return FW_version + + def print_overview(self, update_currents=False): + """ + Pretty-prints the status of the QDac + """ + + self._get_status(readcurrents=update_currents) + + paramstoget = [['i', 'v'], ['irange', 'vrange']] + printdict = {'i': 'Current', 'v': 'Voltage', 'vrange': 'Voltage range', + 'irange': 'Current range'} + + returnmap = {'vrange': {1: '-1 V to 1 V', 10: '-10 V to 10 V'}, + 'irange': {0: '0 to 1 muA', 1: '0 to 100 muA'}} + + # Print the channels + for ii in range(self.num_chans): + line = 'Channel {} \n'.format(ii+1) + line += ' ' + for pp in paramstoget[0]: + param = getattr(self.channels[ii], pp) + line += printdict[pp] + line += ': {}'.format(param.get_latest()) + line += ' ({})'.format(param.unit) + line += '. ' + line += '\n ' + for pp in paramstoget[1]: + param = getattr(self.channels[ii], pp) + line += printdict[pp] + value = param.get_latest() + line += ': {}'.format(returnmap[pp][value]) + line += '. ' + print(line) From e80de665297a6add5e7a995fd1c1eddebdf56b95 Mon Sep 17 00:00:00 2001 From: WilliamHPNielsen Date: Fri, 9 Jun 2017 16:39:43 +0200 Subject: [PATCH 40/49] feat: Add a set method to MultiChannelInstrumentParameter --- qcodes/instrument/channel.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index f94c57f7051b..ad27e808a577 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -349,6 +349,17 @@ def get(self): """ return tuple(chan.parameters[self._param_name].get() for chan in self._channels) + def set(self, value): + """ + Set all parameters to this value + + Args: + value (unknown): The value to set to. The type is given by the + underlying parameter. + """ + for chan in self._channels: + getattr(chan, self._param_name).set(value) + @property def full_names(self): """Overwrite full_names because the instument name is already included in the name. From c90ac7cafce6ce89abcbfaa9fc167a152000c52e Mon Sep 17 00:00:00 2001 From: WilliamHPNielsen Date: Fri, 9 Jun 2017 16:42:30 +0200 Subject: [PATCH 41/49] docs: Add a driver example for the channelised Qdac --- .../Qcodes example with QDac_channels.ipynb | 288 ++++++++++++++++++ 1 file changed, 288 insertions(+) create mode 100644 docs/examples/driver_examples/Qcodes example with QDac_channels.ipynb diff --git a/docs/examples/driver_examples/Qcodes example with QDac_channels.ipynb b/docs/examples/driver_examples/Qcodes example with QDac_channels.ipynb new file mode 100644 index 000000000000..6fc222bd1db8 --- /dev/null +++ b/docs/examples/driver_examples/Qcodes example with QDac_channels.ipynb @@ -0,0 +1,288 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Qcodes example with QDac_channels" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import qcodes as qc\n", + "import numpy as np\n", + "\n", + "from time import sleep\n", + "\n", + "from qcodes.instrument_drivers.QDev.QDac_channels import QDac" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Connect to the instrument\n", + "qdac = QDac('qdac', 'ASRL6::INSTR', update_currents=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Basic QDac Usage\n", + "\n", + "The QCoDeS QDac driver currently supports using\n", + " * 48 Output Channels\n", + " * 3 $\\times$ 6 temperature sensors\n", + "\n", + "Each output channel has six parameters:\n", + " * DC voltage\n", + " * DC voltage range\n", + " * Current out (read-only)\n", + " * Current out range\n", + " * slope\n", + " * sync\n", + "\n", + "The slope is the (maximal) slope in V/s that the channel can allow its voltage to change by. By default, all channels have a slope of \"Inf\". The slope can be changed dynamically, but no more than 8 channels can have a finite slope at any given time (this is due to hardware limitations)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Current out is the current flowing through the channel this is read-only\n", + "print(qdac.ch01.i.get(), qdac.ch01.i.unit)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# The current range can be either 0 to 1 μA or 0 to 100 μA\n", + "print(qdac.ch01.irange.get())\n", + "# This is set with either 0 (0 to 1 μA) or 1 (0 to 100 μA) \n", + "qdac.ch01.irange.set(1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# The DC voltage may directly be set and gotten\n", + "qdac.ch01.v.set(-1)\n", + "print('Channel 1 voltage: {} {}'.format(qdac.ch01.v.get(), qdac.ch01.v.unit))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Multiple channels can be addressed simultaneously via the 'channels' list\n", + "qdac.channels[0:20].v.get()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Similarly, we may set them\n", + "qdac.channels[0:2].v.set(-1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# The maximal voltage change (in V/s) may be set for each channel\n", + "qdac.ch01.slope.set(1)\n", + "qdac.ch02.slope.set(2)\n", + "# An overview may be printed (all other channels have 'Inf' slope)\n", + "qdac.printslopes()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# now setting channel 1 and 2 voltages will cause slow ramps to happen\n", + "qdac.ch01.v.set(0)\n", + "qdac.ch02.v.set(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Note that only 8 (or fewer) channels can have finite slopes at one time\n", + "# To make space for other channels, set the slope to inifite\n", + "qdac.ch01.slope('Inf')\n", + "qdac.printslopes()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# To each channel one may assign a sync channel:\n", + "qdac.ch02.sync(2) # sync output 2 will fire a 10 ms 5 V pulse when ch02 ramps\n", + "# note that even if no visible ramp is performed (i.e. ramping from 1 V to 1 V), a pulse is still fired.\n", + "\n", + "# The sync pulse settings can be modified\n", + "qdac.ch02.sync_delay(0) # The sync pulse delay (s)\n", + "qdac.ch02.sync_duration(25e-3) # The sync pulse duration (s). Default is 10 ms." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "qdac.ch02.v.set(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# syncs are unassigned by assigning sync 0\n", + "qdac.ch02.sync(0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Attention!\n", + "\n", + "The v_range parameter is really controlling a 20 dB (amplitude factor 10) attenuator. Upon changing the vrange, the attenuator is **immediately** applied (or revoked). This will --irrespective of any slope set-- cause an instantaneous voltage change unless the channel voltage is zero. By default, all attenuators are off, and the voltage range is from -10 V to 10 V for all channels." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Here is a small example showing what to look out for\n", + "#\n", + "qdac.ch01.vrange.set(0) # Attenuation OFF (the default)\n", + "qdac.ch01.v.set(1.5)\n", + "qdac.ch01.vrange.set(1) # Attenuation ON\n", + "print(qdac.ch01.v.get()) # Returns 0.15 V\n", + "qdac.ch01.v.set(0.1)\n", + "qdac.ch01.vrange.set(0) # Attenuation OFF\n", + "print(qdac.ch01.v.get()) # returns 1 V" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Overview of channel settings\n", + "\n", + "The driver provides a method for pretty-printing the state of all channels. On startup, all channels are queried for voltage and current across them, but the current query is very slow (blame the hardware).\n", + "\n", + "The pretty-print method may or may not **update** the values for the currents, depending on the value of the `update_currents` flag. Each current reading takes some 200 ms, so updating all current values takes about 10 s." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "qdac.print_overview(update_currents=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Temperature sensors\n", + "\n", + "Physically, the QDac consists of six boards each hosting eight channels. On three locations on each board, a temperature sensors is placed. These provide read-only parameters, named `tempX_Y` where `X` is the board number and `Y` the sensor number." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(qdac.temp0_0.get(), qdac.temp0_0.unit)\n", + "print(qdac.temp2_1.get(), qdac.temp0_0.unit)\n", + "print(qdac.temp5_2.get(), qdac.temp0_0.unit)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "qdac.close()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.3" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} From 1793d19b0cd79e6a5d1b9b0f0041b8cbd7d43a38 Mon Sep 17 00:00:00 2001 From: WilliamHPNielsen Date: Mon, 12 Jun 2017 13:32:06 +0200 Subject: [PATCH 42/49] feat: Make ChannelList take default paramclass Make the paramclass returned by __getattr__ be customisable. --- qcodes/instrument/channel.py | 72 ++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 24 deletions(-) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index ad27e808a577..bd1b7307f9b0 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -91,34 +91,58 @@ def ask_raw(self, cmd): class ChannelList(Metadatable): """ - Container for channelized parameters that allows for sweeps over all channels, as well - as addressing of individual channels. + Container for channelized parameters that allows for sweeps over + all channels, as well as addressing of individual channels. Args: - parent (Instrument): the instrument to which this channel should be attached + parent (Instrument): the instrument to which this channel + should be attached name (string): the name of the channel list - chan_type (InstrumentChannel): the type of channel contained within this list + chan_type (InstrumentChannel): the type of channel contained + within this list + + chan_list (Iterable[chan_type]): An optional iterable of + channels of type chan_type. This will create a list and + immediately lock the ChannelList. - chan_list (Iterable[chan_type]): An optional iterable of channels of type chan_type. - This will create a list and immediately lock the ChannelList. + snapshotable (bool): Optionally disables taking of snapshots + for a given channel list. This is used when objects + stored inside a channel list are accessible in multiple + ways and should not be repeated in an instrument snapshot. - snapshotable (bool): Optionally disables taking of snapshots for a given channel list. - This is used when objects stored inside a channel list are accessible in multiple ways - and should not be repeated in an instrument snapshot. + paramclass (unknown): The class of the object to be returned by + the ChanneList's __getattr__ method. Should be a subclass of + MultiChannelInstrumentParameter. + + Raises: + ValueError: If chan_type is not a subclass of InstrumentChannel + ValueError: If paramclass if not a subclass of + MultiChannelInstrumentParameter (note that a class is a subclass + of itself). """ - def __init__(self, parent, name, chan_type, chan_list=None, snapshotable=True): + def __init__(self, parent, name, chan_type, chan_list=None, + snapshotable=True, + paramclass=MultiChannelInstrumentParameter): super().__init__() self._parent = parent self._name = name - if not isinstance(chan_type, type) or not issubclass(chan_type, InstrumentChannel): - raise ValueError("Channel Lists can only hold instances of type InstrumentChannel") + if (not isinstance(chan_type, type) or + not issubclass(chan_type, InstrumentChannel)): + raise ValueError("Channel Lists can only hold instances of type" + " InstrumentChannel") + if (not isinstance(paramclass, type) or + not issubclass(paramclass, MultiChannelInstrumentParameter)): + raise ValueError("Paramclass must be a (subclass of) " + "MultiChannelInstrumentParameter") + self._chan_type = chan_type self._snapshotable = snapshotable + self._paramclass = paramclass # If a list of channels is not provided, define a list to store channels. # This will eventually become a locked tuple. @@ -301,18 +325,18 @@ def __getattr__(self, name): else: shapes = tuple(() for chan in self._channels) - param = MultiChannelInstrumentParameter(self._channels, - param_name=name, - name="Multi_{}".format(name), - names=names, - shapes=shapes, - instrument=self._parent, - labels=labels, - units=units, - setpoints=setpoints, - setpoint_names=setpoint_names, - setpoint_units=setpoint_units, - setpoint_labels=setpoint_labels) + param = self._paramclass(self._channels, + param_name=name, + name="Multi_{}".format(name), + names=names, + shapes=shapes, + instrument=self._parent, + labels=labels, + units=units, + setpoints=setpoints, + setpoint_names=setpoint_names, + setpoint_units=setpoint_units, + setpoint_labels=setpoint_labels) return param # Check if this is a valid function From a4e4b90844e3cc1b87db89cfbca803cd44883e01 Mon Sep 17 00:00:00 2001 From: WilliamHPNielsen Date: Tue, 13 Jun 2017 16:36:50 +0200 Subject: [PATCH 43/49] feat: Add a channelised QDac driver Add a channelised driver for the QDac. This requires changing the base code for channels a bit to allow for custom MultiChannelInstrumentParameter subclasses. --- qcodes/instrument/channel.py | 92 +++++++++---------- .../instrument_drivers/QDev/QDac_channels.py | 34 ++++++- 2 files changed, 79 insertions(+), 47 deletions(-) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index bd1b7307f9b0..509013e6f5cf 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -89,6 +89,49 @@ def ask_raw(self, cmd): return self._parent.ask_raw(cmd) +class MultiChannelInstrumentParameter(MultiParameter): + """ + Parameter to get or set multiple channels simultaneously. + + Will normally be created by a ChannelList and not directly by anything else. + + Args: + channels(list[chan_type]): A list of channels which we can operate on simultaneously. + + param_name(str): Name of the multichannel parameter + """ + def __init__(self, channels: Union[List, Tuple], param_name, *args, **kwargs): + super().__init__(*args, **kwargs) + self._channels = channels + self._param_name = param_name + + def get(self): + """ + Return a tuple containing the data from each of the channels in the list + """ + return tuple(chan.parameters[self._param_name].get() for chan in self._channels) + + def set(self, value): + """ + Set all parameters to this value + + Args: + value (unknown): The value to set to. The type is given by the + underlying parameter. + """ + for chan in self._channels: + getattr(chan, self._param_name).set(value) + + @property + def full_names(self): + """Overwrite full_names because the instument name is already included in the name. + This happens because the instument name is included in the channel name merged into the + parameter name above. + """ + + return self.names + + class ChannelList(Metadatable): """ Container for channelized parameters that allows for sweeps over @@ -155,8 +198,6 @@ def __init__(self, parent, name, chan_type, chan_list=None, if not all(isinstance(chan, chan_type) for chan in self._channels): raise TypeError("All items in this channel list must be of type {}.".format(chan_type.__name__)) - - def __getitem__(self, i): """ Return either a single channel, or a new ChannelList containing only the specified channels @@ -165,7 +206,9 @@ def __getitem__(self, i): i (int/slice): Either a single channel index or a slice of channels to get """ if isinstance(i, slice): - return ChannelList(self._parent, self._name, self._chan_type, self._channels[i]) + return ChannelList(self._parent, self._name, self._chan_type, + self._channels[i], + paramclass=self._paramclass) return self._channels[i] def __iter__(self): @@ -349,46 +392,3 @@ def multi_func(*args, **kwargs): return multi_func raise AttributeError('\'{}\' object has no attribute \'{}\''.format(self.__class__.__name__, name)) - - -class MultiChannelInstrumentParameter(MultiParameter): - """ - Parameter to get or set multiple channels simultaneously. - - Will normally be created by a ChannelList and not directly by anything else. - - Args: - channels(list[chan_type]): A list of channels which we can operate on simultaneously. - - param_name(str): Name of the multichannel parameter - """ - def __init__(self, channels: Union[List, Tuple], param_name, *args, **kwargs): - super().__init__(*args, **kwargs) - self._channels = channels - self._param_name = param_name - - def get(self): - """ - Return a tuple containing the data from each of the channels in the list - """ - return tuple(chan.parameters[self._param_name].get() for chan in self._channels) - - def set(self, value): - """ - Set all parameters to this value - - Args: - value (unknown): The value to set to. The type is given by the - underlying parameter. - """ - for chan in self._channels: - getattr(chan, self._param_name).set(value) - - @property - def full_names(self): - """Overwrite full_names because the instument name is already included in the name. - This happens because the instument name is included in the channel name merged into the - parameter name above. - """ - - return self.names diff --git a/qcodes/instrument_drivers/QDev/QDac_channels.py b/qcodes/instrument_drivers/QDev/QDac_channels.py index 0b361b86814f..f010183c87e9 100644 --- a/qcodes/instrument_drivers/QDev/QDac_channels.py +++ b/qcodes/instrument_drivers/QDev/QDac_channels.py @@ -11,6 +11,7 @@ from collections import OrderedDict from qcodes.instrument.channel import InstrumentChannel, ChannelList +from qcodes.instrument.channel import MultiChannelInstrumentParameter from qcodes.instrument.parameter import ManualParameter from qcodes.instrument.visa import VisaInstrument from qcodes.utils import validators as vals @@ -104,6 +105,36 @@ def __init__(self, parent, name, channum): ) +class QDacMultiChannelParameter(MultiChannelInstrumentParameter): + """ + The class to be returned by __getattr__ of the ChannelList. Here customised + for fast multi-readout of voltages. + """ + def __init__(self, channels, param_name, *args, **kwargs): + super().__init__(channels, param_name, *args, **kwargs) + + def get(self): + """ + Return a tuple containing the data from each of the channels in the + list. + """ + # For voltages, we can do something slightly faster than the naive + # approach + + if self._param_name == 'v': + qdac = self._channels[0]._parent + qdac._get_status(readcurrents=False) + output = tuple(chan.parameters[self._param_name].get_latest() + for chan in self._channels) + + # call _instrument.get_status() once and then get_latest + else: + output = tuple(chan.parameters[self._param_name].get() + for chan in self._channels) + + return output + + class QDac(VisaInstrument): """ Channelised driver for the QDev digital-analog converter QDac @@ -171,7 +202,8 @@ def __init__(self, name, address, num_chans=48, update_currents=True): self.channel_validator = vals.Ints(1, self.num_chans) channels = ChannelList(self, "Channels", QDacChannel, - snapshotable=False) + snapshotable=False, + paramclass=QDacMultiChannelParameter) for i in self.chan_range: channel = QDacChannel(self, 'chan{}'.format(i), i) From 034fbf5a3cd54a3bc7e3c606996294fd02abaf66 Mon Sep 17 00:00:00 2001 From: WilliamHPNielsen Date: Tue, 13 Jun 2017 16:54:15 +0200 Subject: [PATCH 44/49] docs: Add notebook for QDac w. channels --- .../Qcodes example with QDac_channels.ipynb | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/examples/driver_examples/Qcodes example with QDac_channels.ipynb b/docs/examples/driver_examples/Qcodes example with QDac_channels.ipynb index 6fc222bd1db8..6246a121d4fd 100644 --- a/docs/examples/driver_examples/Qcodes example with QDac_channels.ipynb +++ b/docs/examples/driver_examples/Qcodes example with QDac_channels.ipynb @@ -24,7 +24,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "# Connect to the instrument\n", @@ -121,7 +123,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "# now setting channel 1 and 2 voltages will cause slow ramps to happen\n", @@ -144,7 +148,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "# To each channel one may assign a sync channel:\n", @@ -168,7 +174,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "# syncs are unassigned by assigning sync 0\n", From 83a32e88b0ecdf7a0cc578e93f819f6e19fd3d62 Mon Sep 17 00:00:00 2001 From: WilliamHPNielsen Date: Wed, 14 Jun 2017 11:35:02 +0200 Subject: [PATCH 45/49] style: Remove unnecessary comment --- qcodes/instrument_drivers/QDev/QDac_channels.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/qcodes/instrument_drivers/QDev/QDac_channels.py b/qcodes/instrument_drivers/QDev/QDac_channels.py index f010183c87e9..c4169a5df757 100644 --- a/qcodes/instrument_drivers/QDev/QDac_channels.py +++ b/qcodes/instrument_drivers/QDev/QDac_channels.py @@ -126,8 +126,6 @@ def get(self): qdac._get_status(readcurrents=False) output = tuple(chan.parameters[self._param_name].get_latest() for chan in self._channels) - - # call _instrument.get_status() once and then get_latest else: output = tuple(chan.parameters[self._param_name].get() for chan in self._channels) From 2dcc9324017d7dad929a6c84be704ac139b8821f Mon Sep 17 00:00:00 2001 From: Sebastian Pauka Date: Thu, 15 Jun 2017 21:27:47 +1000 Subject: [PATCH 46/49] Fix syntax/spelling errors in DAC driver --- qcodes/instrument_drivers/Harvard/Decadac.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/qcodes/instrument_drivers/Harvard/Decadac.py b/qcodes/instrument_drivers/Harvard/Decadac.py index edeeb55db39b..62d5b8d560a0 100644 --- a/qcodes/instrument_drivers/Harvard/Decadac.py +++ b/qcodes/instrument_drivers/Harvard/Decadac.py @@ -47,7 +47,7 @@ def _set_slot(self): """ resp = self.ask_raw("B{};".format(self._slot)) if int(self._dac_parse(resp)) != self._slot: - raise DACException("Unexpected return from DAC when setting slot: {}. DAC slot may not have been set.".format(response)) + raise DACException("Unexpected return from DAC when setting slot: {}. DAC slot may not have been set.".format(resp)) def _set_channel(self): """ @@ -55,7 +55,7 @@ def _set_channel(self): """ resp = self.ask_raw("B{};C{};".format(self._slot, self._channel)) if resp.strip() != "B{}!C{}!".format(self._slot, self._channel): - raise DACException("Unexpected return from DAC when setting channel: {}. DAC channel may not have been set.".format(response)) + raise DACException("Unexpected return from DAC when setting channel: {}. DAC channel may not have been set.".format(resp)) def _query_address(self, addr, count=1, versa_eeprom=False): """ @@ -211,7 +211,7 @@ def __init__(self, parent, name, channel, min_val=-5, max_val=5): get_cmd=partial(self._query_address, _INITIAL_ADDR[self._channel], versa_eeprom=True), get_parser=self._dac_code_to_v, set_cmd=partial(self._write_address, _INITIAL_ADDR[self._channel], versa_eeprom=True), - set_parser=set._dac_v_to_code, vals=vals.Numbers(self.min_val, self.max_val)) + set_parser=self._dac_v_to_code, vals=vals.Numbers(self.min_val, self.max_val)) def _ramp(self, val, rate, block=True): """ @@ -396,10 +396,7 @@ def __init__(self, name, address, min_val=-5, max_val=5, **kwargs): self.add_submodule("slots", slots) self.add_submodule("channels", channels) - # Custom connect message, since we can't identify - t = time() - (self._t0) - print("Connected to Harvard DecaDAC (hw ver: {}, serial: {}) in {:.2f}s".format( - self.version, self.serial_no, t)) + self.connect_message() def set_all(self, volt): """ @@ -443,7 +440,7 @@ def get_idn(self): """ self._feature_detect() - return {serial: self.serial_no, hardware_version: self.version} + return {"serial": self.serial_no, "hardware_version": self.version} def connect_message(self, idn_param='IDN', begin_time=None): """ @@ -456,7 +453,7 @@ def connect_message(self, idn_param='IDN', begin_time=None): """ # start with an empty dict, just in case an instrument doesn't # heed our request to return all 4 fields. - t = time.time() - (begin_time or self._t0) + t = time() - (begin_time or self._t0) con_msg = ("Connected to Harvard DecaDAC " "(hw ver: {}, serial: {}) in {:.2f}s".format( @@ -513,4 +510,4 @@ def write(self, cmd): as well, otherwise commands receive the wrong response. Hence all writes must also read a response. """ - return self.ask(cmd) \ No newline at end of file + return self.ask(cmd) From 84b663b27939e7122a48a3c3f6416ea838cbcbae Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Thu, 15 Jun 2017 15:31:07 +0200 Subject: [PATCH 47/49] Fix: qdac_channels remove magic number --- qcodes/instrument_drivers/QDev/QDac_channels.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/qcodes/instrument_drivers/QDev/QDac_channels.py b/qcodes/instrument_drivers/QDev/QDac_channels.py index c4169a5df757..03ee6987be9b 100644 --- a/qcodes/instrument_drivers/QDev/QDac_channels.py +++ b/qcodes/instrument_drivers/QDev/QDac_channels.py @@ -240,6 +240,7 @@ def __init__(self, name, address, num_chans=48, update_currents=True): self._get_status(readcurrents=update_currents) log.info('[+] Done') + self._output_n_lines = 50 ######################### # Channel gets/sets ######################### @@ -639,14 +640,14 @@ def connect_message(self): self.visa_handle.read())) # take care of the rest of the output - for ii in range(50): + for ii in range(self._output_n_lines): self.visa_handle.read() def _get_firmware_version(self): self.write('status') FW_str = self._write_response FW_version = float(FW_str.replace('Software Version: ', '')) - for ii in range(50): + for ii in range(self._output_n_lines): self.read() return FW_version From 8a44cd40ef45ad10c8cc96f1528999f0a93fa38d Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Thu, 15 Jun 2017 15:34:19 +0200 Subject: [PATCH 48/49] Doc: clarify doc string --- qcodes/instrument/channel.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index 509013e6f5cf..2404f90c76b1 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -155,9 +155,9 @@ class ChannelList(Metadatable): stored inside a channel list are accessible in multiple ways and should not be repeated in an instrument snapshot. - paramclass (unknown): The class of the object to be returned by - the ChanneList's __getattr__ method. Should be a subclass of - MultiChannelInstrumentParameter. + paramclass (MultiChannelInstrumentParameter): The class of the object + to be returned by the ChanneList's __getattr__ method. Should be a + subclass of MultiChannelInstrumentParameter. Raises: ValueError: If chan_type is not a subclass of InstrumentChannel From 24f65a249f2c172b4b02b8466cc5506c0055731d Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Thu, 15 Jun 2017 15:37:03 +0200 Subject: [PATCH 49/49] Fix: rename paramclass to be more specific --- qcodes/instrument/channel.py | 16 ++++++++-------- qcodes/instrument_drivers/QDev/QDac_channels.py | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index 2404f90c76b1..7e50489ce7a7 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -155,13 +155,13 @@ class ChannelList(Metadatable): stored inside a channel list are accessible in multiple ways and should not be repeated in an instrument snapshot. - paramclass (MultiChannelInstrumentParameter): The class of the object + multichan_paramclass (MultiChannelInstrumentParameter): The class of the object to be returned by the ChanneList's __getattr__ method. Should be a subclass of MultiChannelInstrumentParameter. Raises: ValueError: If chan_type is not a subclass of InstrumentChannel - ValueError: If paramclass if not a subclass of + ValueError: If multichan_paramclass if not a subclass of MultiChannelInstrumentParameter (note that a class is a subclass of itself). @@ -169,7 +169,7 @@ class ChannelList(Metadatable): def __init__(self, parent, name, chan_type, chan_list=None, snapshotable=True, - paramclass=MultiChannelInstrumentParameter): + multichan_paramclass=MultiChannelInstrumentParameter): super().__init__() self._parent = parent @@ -178,14 +178,14 @@ def __init__(self, parent, name, chan_type, chan_list=None, not issubclass(chan_type, InstrumentChannel)): raise ValueError("Channel Lists can only hold instances of type" " InstrumentChannel") - if (not isinstance(paramclass, type) or - not issubclass(paramclass, MultiChannelInstrumentParameter)): - raise ValueError("Paramclass must be a (subclass of) " + if (not isinstance(multichan_paramclass, type) or + not issubclass(multichan_paramclass, MultiChannelInstrumentParameter)): + raise ValueError("multichan_paramclass must be a (subclass of) " "MultiChannelInstrumentParameter") self._chan_type = chan_type self._snapshotable = snapshotable - self._paramclass = paramclass + self._paramclass = multichan_paramclass # If a list of channels is not provided, define a list to store channels. # This will eventually become a locked tuple. @@ -208,7 +208,7 @@ def __getitem__(self, i): if isinstance(i, slice): return ChannelList(self._parent, self._name, self._chan_type, self._channels[i], - paramclass=self._paramclass) + multichan_paramclass=self._paramclass) return self._channels[i] def __iter__(self): diff --git a/qcodes/instrument_drivers/QDev/QDac_channels.py b/qcodes/instrument_drivers/QDev/QDac_channels.py index 03ee6987be9b..9ab3f3d7b27a 100644 --- a/qcodes/instrument_drivers/QDev/QDac_channels.py +++ b/qcodes/instrument_drivers/QDev/QDac_channels.py @@ -201,7 +201,7 @@ def __init__(self, name, address, num_chans=48, update_currents=True): channels = ChannelList(self, "Channels", QDacChannel, snapshotable=False, - paramclass=QDacMultiChannelParameter) + multichan_paramclass=QDacMultiChannelParameter) for i in self.chan_range: channel = QDacChannel(self, 'chan{}'.format(i), i)