diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 3ab4fb14e7e..901f59ea6d9 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -433,7 +433,9 @@ class ArrayParameter(_BaseParameter): per setpoint array. Ignored if a setpoint is a DataArray, which already has a label. - TODO (alexcjohnson) we need setpoint_units (and in MultiParameter) + setpoint_units (Optional[Tuple[str]]): one label (like ``v``) + per setpoint array. Ignored if a setpoint is a DataArray, which + already has a unit. docstring (Optional[str]): documentation string for the __doc__ field of the object. The __doc__ field of the instance is used by @@ -448,7 +450,7 @@ class ArrayParameter(_BaseParameter): def __init__(self, name, shape, instrument=None, label=None, unit=None, units=None, setpoints=None, setpoint_names=None, setpoint_labels=None, - docstring=None, snapshot_get=True, metadata=None): + setpoint_units=None, docstring=None, snapshot_get=True, metadata=None): super().__init__(name, instrument, snapshot_get, metadata) if self.has_set: # TODO (alexcjohnson): can we support, ala Combine? @@ -488,10 +490,15 @@ def __init__(self, name, shape, instrument=None, not is_sequence_of(setpoint_labels, (nt, str), shape=sp_shape)): raise ValueError('setpoint_labels must be a tuple of strings') + if (setpoint_units is not None and + not is_sequence_of(setpoint_units, (nt, str), + shape=sp_shape)): + raise ValueError('setpoint_units must be a tuple of strings') self.setpoints = setpoints self.setpoint_names = setpoint_names self.setpoint_labels = setpoint_labels + self.setpoint_units = setpoint_units self.__doc__ = os.linesep.join(( 'Parameter class:', @@ -591,6 +598,10 @@ class MultiParameter(_BaseParameter): ``labels``) per setpoint array. Ignored if a setpoint is a DataArray, which already has a label. + setpoint_units (Optional[Tuple[Tuple[str]]]): one unit (like + ``V``) per setpoint array. Ignored if a setpoint is a + DataArray, which already has a unit. + docstring (Optional[str]): documentation string for the __doc__ field of the object. The __doc__ field of the instance is used by some help systems, but not all @@ -604,6 +615,7 @@ class MultiParameter(_BaseParameter): def __init__(self, name, names, shapes, instrument=None, labels=None, units=None, setpoints=None, setpoint_names=None, setpoint_labels=None, + setpoint_units=None, docstring=None, snapshot_get=True, metadata=None): super().__init__(name, instrument, snapshot_get, metadata) @@ -643,9 +655,14 @@ def __init__(self, name, names, shapes, instrument=None, raise ValueError( 'setpoint_labels must be a tuple of tuples of strings') + if not _is_nested_sequence_or_none(setpoint_units, (nt, str), shapes): + raise ValueError( + 'setpoint_units must be a tuple of tuples of strings') + self.setpoints = setpoints self.setpoint_names = setpoint_names self.setpoint_labels = setpoint_labels + self.setpoint_units = setpoint_units self.__doc__ = os.linesep.join(( 'MultiParameter class:', diff --git a/qcodes/loops.py b/qcodes/loops.py index 0b5c554c63c..dee3f2453b1 100644 --- a/qcodes/loops.py +++ b/qcodes/loops.py @@ -553,18 +553,27 @@ def _parameter_arrays(self, action): action_indices = ((),) else: raise ValueError('a gettable parameter must have .name or .names') - + if hasattr(action, 'names') and hasattr(action, 'units'): + units = action.units + elif hasattr(action, 'unit'): + units = (action.unit,) + else: + units = tuple(['']*len(names)) num_arrays = len(names) + num_units = len(units) + assert num_arrays == num_units shapes = getattr(action, 'shapes', None) sp_vals = getattr(action, 'setpoints', None) sp_names = getattr(action, 'setpoint_names', None) sp_labels = getattr(action, 'setpoint_labels', None) + sp_units = getattr(action, 'setpoint_units', None) if shapes is None: shapes = (getattr(action, 'shape', ()),) * num_arrays sp_vals = (sp_vals,) * num_arrays sp_names = (sp_names,) * num_arrays sp_labels = (sp_labels,) * num_arrays + sp_units = (sp_units,) * num_arrays else: sp_blank = (None,) * num_arrays # _fill_blank both supplies defaults and tests length @@ -573,26 +582,28 @@ def _parameter_arrays(self, action): sp_vals = self._fill_blank(sp_vals, sp_blank) sp_names = self._fill_blank(sp_names, sp_blank) sp_labels = self._fill_blank(sp_labels, sp_blank) + sp_units = self._fill_blank(sp_units, sp_blank) # now loop through these all, to make the DataArrays # record which setpoint arrays we've made, so we don't duplicate all_setpoints = {} - for name, full_name, label, shape, i, sp_vi, sp_ni, sp_li in zip( - names, full_names, labels, shapes, action_indices, - sp_vals, sp_names, sp_labels): + for name, full_name, label, unit, shape, i, sp_vi, sp_ni, sp_li, sp_ui in zip( + names, full_names, labels, units, shapes, action_indices, + sp_vals, sp_names, sp_labels, sp_units): if shape is None or shape == (): - shape, sp_vi, sp_ni, sp_li = (), (), (), () + shape, sp_vi, sp_ni, sp_li, sp_ui= (), (), (), (), () else: sp_blank = (None,) * len(shape) sp_vi = self._fill_blank(sp_vi, sp_blank) sp_ni = self._fill_blank(sp_ni, sp_blank) sp_li = self._fill_blank(sp_li, sp_blank) + sp_ui = self._fill_blank(sp_ui, sp_blank) setpoints = () # loop through dimensions of shape to make the setpoint arrays - for j, (vij, nij, lij) in enumerate(zip(sp_vi, sp_ni, sp_li)): - sp_def = (shape[: 1 + j], j, setpoints, vij, nij, lij) + for j, (vij, nij, lij, uij) in enumerate(zip(sp_vi, sp_ni, sp_li, sp_ui)): + sp_def = (shape[: 1 + j], j, setpoints, vij, nij, lij, uij) if sp_def not in all_setpoints: all_setpoints[sp_def] = self._make_setpoint_array(*sp_def) out.append(all_setpoints[sp_def]) @@ -600,7 +611,7 @@ def _parameter_arrays(self, action): # finally, make the output data array with these setpoints out.append(DataArray(name=name, full_name=full_name, label=label, - shape=shape, action_indices=i, + shape=shape, action_indices=i, unit=unit, set_arrays=setpoints, parameter=action)) return out @@ -614,7 +625,7 @@ def _fill_blank(self, inputs, blanks): raise ValueError('Wrong number of inputs supplied') def _make_setpoint_array(self, shape, i, prev_setpoints, vals, name, - label): + label, unit): if vals is None: vals = self._default_setpoints(shape) elif isinstance(vals, DataArray): @@ -642,7 +653,7 @@ def _make_setpoint_array(self, shape, i, prev_setpoints, vals, name, name = 'index{}'.format(i) return DataArray(name=name, label=label, set_arrays=prev_setpoints, - shape=shape, preset_data=vals) + shape=shape, preset_data=vals, unit=unit) def _default_setpoints(self, shape): if len(shape) == 1: