Qcodes: problem with station and meas.run():

when adding station to the measurement then using the ‘with meas.run( ) as datasaver’ the station does not seem to work. further the station object does not want to return a snapshot either after running the meas.run(), even though it returned it fine earlier in the code.

Here is all the code:

import Qcodes_Keithley213Driver_2 as kd
import qcodes_fluke8842aDriver_2 as fd
import numpy as np
import matplotlib.pyplot as plt
import time

import tempfile
import os

import qcodes
from qcodes import initialise_or_create_database_at, \
    load_or_create_experiment, Measurement, Parameter
from qcodes.dataset.plotting import plot_dataset
from qcodes.tests.instrument_mocks import (
    DummyInstrument,
    DummyInstrumentWithMeasurement
)


keithSource.close()
flukeMeter.close()


keithSource = kd.Keithley213Driver('keithSource','GPIB0::9::INSTR')
flukeMeter = fd.Fluke8842aDriver('fDMMInst1','GPIB0::10::INSTR')
## dac = DummyInstrument('dac', gates=['ch1', 'ch2'])

station = qcodes.Station()
## station.add_component(dac)
## station.add_component(keithSource)
## station.add_component(flukeMeter)
## _=station.snapshot()
## print(_)                                               #returns fine here


initialise_or_create_database_at('C:\\Users\\ehb21173\\Desktop\\transistor test 2\\transistor_tests_2.db')
qcodes.config.core.db_location
exp = load_or_create_experiment(experiment_name='transistor test 1_ IV plots', sample_name='test on 2n7000_2')
meas = Measurement(exp=exp, station=station, name='transistor tests name')

meas.register_parameter(keithSource.ch1.voltage)
meas.register_parameter(keithSource.ch2.voltage)
meas.register_parameter(flukeMeter.voltageDC, setpoints=(keithSource.ch1.voltage, keithSource.ch2.voltage))

keithSource.ch1.maxV(10) # gate
keithSource.ch2.maxV(10) # source
sourceVoltage = np.linspace(0,10,2,endpoint=True)
gateVoltage = np.linspace(1,4,2,endpoint=True)

meas.write_period = 1

with meas.run() as datasaver:
    for j in range(len(gateVoltage)):
        keithSource.ch1.voltage(gateVoltage[j])  # gate

        for i in range(len(sourceVoltage)):
            keithSource.ch2.voltage(sourceVoltage[i]) # source
            time.sleep(3)
            voltage = flukeMeter.voltageDC()
            datasaver.add_result((keithSource.ch1.voltage, gateVoltage[j]),
                                 (keithSource.ch2.voltage, sourceVoltage[i]),
                                 (flukeMeter.voltageDC, voltage))

        time.sleep(3)
        keithSource.ch2.voltage(0)
        keithSource.ch1.voltage(0)
        time.sleep(3)
dataset = datasaver.dataset

keithSource.ch2.voltage(0)
keithSource.ch1.voltage(0)
keithSource.close()
flukeMeter.close()

####################################################################################################

the error returned is:

RecursionError                            Traceback (most recent call last)
Input In [138], in <cell line: 9>()
      5 # current = np.empty([len(gateVoltage),len(sourceVoltage)])
      7 meas.write_period = 1
----> 9 with meas.run() as datasaver:
     10     for j in range(len(gateVoltage)):
     11         keithSource.ch1.voltage(gateVoltage[j])  # gate

File ~\.conda\envs\drivers\lib\site-packages\qcodes\dataset\measurements.py:601, in Runner.__enter__(self)
    598 else:
    599     snapshot = {}
--> 601 self.ds.prepare(
    602     snapshot=snapshot,
    603     interdeps=self._interdependencies,
    604     write_in_background=self._write_in_background,
    605     shapes=self._shapes,
    606     parent_datasets=self._parent_datasets,
    607 )
    609 # register all subscribers
    610 if isinstance(self.ds, DataSet):

File ~\.conda\envs\drivers\lib\site-packages\qcodes\dataset\data_set.py:323, in DataSet.prepare(self, snapshot, interdeps, shapes, parent_datasets, write_in_background)
    313 def prepare(
    314     self,
    315     *,
   (...)
    320     write_in_background: bool = False,
    321 ) -> None:
--> 323     self.add_snapshot(json.dumps({"station": snapshot}, cls=NumpyJSONEncoder))
    325     if interdeps == InterDependencies_():
    326         raise RuntimeError("No parameters supplied")

File ~\.conda\envs\drivers\lib\json\__init__.py:238, in dumps(obj, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, default, sort_keys, **kw)
    232 if cls is None:
    233     cls = JSONEncoder
    234 return cls(
    235     skipkeys=skipkeys, ensure_ascii=ensure_ascii,
    236     check_circular=check_circular, allow_nan=allow_nan, indent=indent,
    237     separators=separators, default=default, sort_keys=sort_keys,
--> 238     **kw).encode(obj)

File ~\.conda\envs\drivers\lib\json\encoder.py:199, in JSONEncoder.encode(self, o)
    195         return encode_basestring(o)
    196 # This doesn't pass the iterator directly to ''.join() because the
    197 # exceptions aren't as detailed.  The list call should be roughly
    198 # equivalent to the PySequence_Fast that ''.join() would do.
--> 199 chunks = self.iterencode(o, _one_shot=True)
    200 if not isinstance(chunks, (list, tuple)):
    201     chunks = list(chunks)

File ~\.conda\envs\drivers\lib\json\encoder.py:257, in JSONEncoder.iterencode(self, o, _one_shot)
    252 else:
    253     _iterencode = _make_iterencode(
    254         markers, self.default, _encoder, self.indent, floatstr,
    255         self.key_separator, self.item_separator, self.sort_keys,
    256         self.skipkeys, _one_shot)
--> 257 return _iterencode(o, 0)

File ~\.conda\envs\drivers\lib\site-packages\qcodes\utils\helpers.py:96, in NumpyJSONEncoder.default(self, obj)
     93 elif isinstance(obj, np.ndarray):
     94     # for numpy arrays
     95     return obj.tolist()
---> 96 elif (isinstance(obj, numbers.Complex) and
     97       not isinstance(obj, numbers.Real)):
     98     return {
     99         '__dtype__': 'complex',
    100         're': float(obj.real),
    101         'im': float(obj.imag)
    102     }
    103 elif isinstance(obj, uncertainties.UFloat):

File ~\.conda\envs\drivers\lib\abc.py:119, in ABCMeta.__instancecheck__(cls, instance)
    117 def __instancecheck__(cls, instance):
    118     """Override for isinstance(instance, cls)."""
--> 119     return _abc_instancecheck(cls, instance)

RecursionError: maximum recursion depth exceeded in comparison

################################################################################################

when not adding station to the measurment everything works fine but, if I try to do station.snapshot() after the meas.run() i get the following error:

AttributeError                            Traceback (most recent call last)
Input In [145], in <cell line: 1>()
----> 1 flukeMeter.print_readable_snapshot()

File ~\.conda\envs\drivers\lib\site-packages\qcodes\instrument\base.py:296, in InstrumentBase.print_readable_snapshot(self, update, max_chars)
    280 """
    281 Prints a readable version of the snapshot.
    282 The readable snapshot includes the name, value and unit of each
   (...)
    293         Defaults to 80 to be consistent with default terminal width.
    294 """
    295 floating_types = (float, np.integer, np.floating)
--> 296 snapshot = self.snapshot(update=update)
    298 par_lengths = [len(p) for p in snapshot['parameters']]
    300 # Min of 50 is to prevent a super long parameter name to break this
    301 # function

File ~\.conda\envs\drivers\lib\site-packages\qcodes\utils\metadata.py:64, in Metadatable.snapshot(self, update)
     51 def snapshot(self, update: Optional[bool] = False) -> Dict[Any, Any]:
     52     """
     53     Decorate a snapshot dictionary with metadata.
     54     DO NOT override this method if you want metadata in the snapshot
   (...)
     61         Base snapshot.
     62     """
---> 64     snap = self.snapshot_base(update=update)
     66     if len(self.metadata):
     67         snap['metadata'] = self.metadata

File ~\.conda\envs\drivers\lib\site-packages\qcodes\instrument\visa.py:261, in VisaInstrument.snapshot_base(self, update, params_to_skip_update)
    239 def snapshot_base(self, update: Optional[bool] = True,
    240                   params_to_skip_update: Optional[Sequence[str]] = None
    241                   ) -> Dict[Any, Any]:
    242     """
    243     State of the instrument as a JSON-compatible dict (everything that
    244     the custom JSON encoder class :class:`qcodes.utils.helpers.NumpyJSONEncoder`
   (...)
    259         dict: base snapshot
    260     """
--> 261     snap = super().snapshot_base(update=update,
    262                                  params_to_skip_update=params_to_skip_update)
    264     snap['address'] = self._address
    265     snap['terminator'] = self._terminator

File ~\.conda\envs\drivers\lib\site-packages\qcodes\instrument\base.py:250, in InstrumentBase.snapshot_base(self, update, params_to_skip_update)
    245 if params_to_skip_update is None:
    246     params_to_skip_update = []
    248 snap: Dict[str, Any] = {
    249     "functions": {name: func.snapshot(update=update)
--> 250                   for name, func in self.functions.items()},
    251     "submodules": {name: subm.snapshot(update=update)
    252                    for name, subm in self.submodules.items()},
    253     "parameters": {},
    254     "__class__": full_class(self)
    255 }
    257 for name, param in self.parameters.items():
    258     if param.snapshot_exclude:

File ~\.conda\envs\drivers\lib\site-packages\qcodes\utils\helpers.py:417, in DelegateAttributes.__getattr__(self, key)
    414 for name in self.delegate_attr_dicts:
    415     if key == name:
    416         # needed to prevent infinite loops!
--> 417         raise AttributeError(
    418             "dict '{}' has not been created in object '{}'".format(
    419                 key, self.__class__.__name__))
    420     try:
    421         d = getattr(self, name, None)

AttributeError: dict 'functions' has not been created in object 'Fluke8842aDriver'

################################################################################################

Note that when adding the dummy instrument to the station instead everything works fine so it may be an issue with the driver of the instrument (adding either to the snapshot without the other produces the same issue), here is the driver for the multimeter:

class voltageDC(Parameter):
    def __init__(self, name, *args, **kwargs):
        # only name is required
        super().__init__(name,
                         docstring='command for measurement of voltage DC', *args, **kwargs)

    # you must provide a get method, a set method, or both.
    def get_raw(self):
        if self.root_instrument.currentMeasConfig != 'V DC':
            self.root_instrument.visa_handle.write('F1')
            self.root_instrument.currentMeasConfig = 'V DC'

        self.root_instrument.visa_handle.write('?')
        return self.root_instrument.visa_handle.read_raw()

class CurrentDC(Parameter):
    def __init__(self, name, *args, **kwargs):
        # only name is required
        super().__init__(name,
                         docstring='command for measurement of voltage DC', *args, **kwargs)

    # you must provide a get method, a set method, or both.
    def get_raw(self):
        if self.root_instrument.currentMeasConfig != 'A DC':
            self.root_instrument.visa_handle.write('F5')
            self.root_instrument.currentMeasConfig = 'A DC'

        self.root_instrument.visa_handle.write('?')
        return self.root_instrument.visa_handle.read_raw()


class rate(Parameter):
    def __init__(self, name, *args, **kwargs):
        # only name is required
        super().__init__(name,
                         docstring='command for measurement of voltage DC', *args, **kwargs)

    # you must provide a get method, a set method, or both.
    def get_raw(self):
        self.root_instrument.visa_handle.write("G0")
        config = self.root_instrument.visa_handle.read_raw()
        return self.root_instrument.list_of_configs_S[int(str((int(config)))[2])] #change this to dictionary so easier to read

    def set_raw(self, speed):
        index = self.root_instrument.list_of_configs_S.index(speed)
        self.root_instrument.visa_handle.write("S"+str(index))



class Fluke8842aDriver(VisaInstrument):

    def __init__(self, name, address, *args,**kwargs):
        super().__init__(name, address, terminator="\r\n", *args,**kwargs)

        self.currentMeasConfig = 'V DC'
   

        self.list_of_configs_S = ["slow", "med","fast"]

        self.add_parameter('voltageDC', parameter_class=voltageDC,get_parser=float, unit='V')
        self.add_parameter('currentDC', parameter_class=CurrentDC,get_parser=float, unit='A')
        self.add_parameter('rate', parameter_class=rate)

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 37 (7 by maintainers)

Most upvoted comments

I can reproduce this now

import json
from qcodes.utils.helpers import NumpyJSONEncoder

a = {        "voltageDC": {
            "__class__": "qcodes_fluke8842aDriver_2.voltageDC",
            "full_name": "fDMMInst1_voltageDC",
            "value": 5.7e-05,
            "ts": "2022-09-12 10:18:34",
            "raw_value": b"+000.057E-3\r\n",
            "name": "voltageDC",
            "unit": "V",
            "inter_delay": 0,
            "post_delay": 0,
            "instrument": "qcodes_fluke8842aDriver_2.Fluke8842aDriver",
            "instrument_name": "fDMMInst1",
            "label": "voltageDC",
        }}
json.dumps(a,  cls=NumpyJSONEncoder)

Triggers the issue.

The reason is the bytestring in the raw_value. The reason why you are seeing a bytestring here (note the b in from of the string) is that you use visa_handle.read_raw. As @astafan8 suggested above using instrument.ask is a lot better or at the very least instrument.visa_handle.read and or instrument.visa_handle.query read_raw should really only be used when you handle the decoding or the raw value in your get_raw your self for strange dataformats that cannot be automatically decoded.

That being said qcodes should handle bytestrings in snapshots better.

@jenshnielsen same problem ‘maximum recursion depth exceeded in comparison’.

@jenshnielsen sure, thanks for al the help!

@jenshnielsen here is the code:

flukeMeter = fd.Fluke8842aDriver('flukeMeter','GPIB0::10::INSTR')

for param_name, param in flukeMeter.parameters.items():
    print(f"encoding {param_name}")
    json.dumps(param.snapshot(update=True))

and the output:

encoding IDN
encoding timeout
encoding voltageDC
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Input In [23], in <cell line: 3>()
      3 for param_name, param in flukeMeter.parameters.items():
      4     print(f"encoding {param_name}")
----> 5     json.dumps(param.snapshot(update=True))

File ~\.conda\envs\drivers\lib\json\__init__.py:231, in dumps(obj, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, default, sort_keys, **kw)
    226 # cached encoder
    227 if (not skipkeys and ensure_ascii and
    228     check_circular and allow_nan and
    229     cls is None and indent is None and separators is None and
    230     default is None and not sort_keys and not kw):
--> 231     return _default_encoder.encode(obj)
    232 if cls is None:
    233     cls = JSONEncoder

File ~\.conda\envs\drivers\lib\json\encoder.py:199, in JSONEncoder.encode(self, o)
    195         return encode_basestring(o)
    196 # This doesn't pass the iterator directly to ''.join() because the
    197 # exceptions aren't as detailed.  The list call should be roughly
    198 # equivalent to the PySequence_Fast that ''.join() would do.
--> 199 chunks = self.iterencode(o, _one_shot=True)
    200 if not isinstance(chunks, (list, tuple)):
    201     chunks = list(chunks)

File ~\.conda\envs\drivers\lib\json\encoder.py:257, in JSONEncoder.iterencode(self, o, _one_shot)
    252 else:
    253     _iterencode = _make_iterencode(
    254         markers, self.default, _encoder, self.indent, floatstr,
    255         self.key_separator, self.item_separator, self.sort_keys,
    256         self.skipkeys, _one_shot)
--> 257 return _iterencode(o, 0)

File ~\.conda\envs\drivers\lib\json\encoder.py:179, in JSONEncoder.default(self, o)
    160 def default(self, o):
    161     """Implement this method in a subclass such that it returns
    162     a serializable object for ``o``, or calls the base implementation
    163     (to raise a ``TypeError``).
   (...)
    177 
    178     """
--> 179     raise TypeError(f'Object of type {o.__class__.__name__} '
    180                     f'is not JSON serializable')

TypeError: Object of type bytes is not JSON serializable

Could you try json encoding the snapshot of each of the parameters Something like

for param_name, param in flukemeter.parameters.items()
    print(f"encoding {param_name}")
    json.dumps(param.shapshot(update=True))

@astafan8 yep nevermind, same problem after adding the instruments to the station (its both of them btw).

@astafan8 yep still the same problem.

Note that the value of 1.00071e21 also shows up as the vendor of the instrument so it seems like there is a potential issue with the instrumets implementation of *IDN too