Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Smaller changes to parameters #788

Merged
merged 25 commits into from
Oct 16, 2017
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
091f9a3
fix:save scaled value, perform initial get
nulinspiratie Oct 3, 2017
983925f
fix: self.get return raw_value when possible
nulinspiratie Oct 3, 2017
f2d0d16
fix: the get function when get_cmd=None did not take any scaling into…
nulinspiratie Oct 3, 2017
ecf3d17
fix: pop raw_value when snapshot_value=False
nulinspiratie Oct 3, 2017
b466430
fix: return self._latest['value'] if self.scale is None
nulinspiratie Oct 3, 2017
0aaed21
fix:save scaled value, perform initial get
nulinspiratie Oct 3, 2017
7358753
fix: self.get return raw_value when possible
nulinspiratie Oct 3, 2017
5a4f0ea
fix: the get function when get_cmd=None did not take any scaling into…
nulinspiratie Oct 3, 2017
b3e566a
fix: pop raw_value when snapshot_value=False
nulinspiratie Oct 3, 2017
f19aeec
fix: return self._latest['value'] if self.scale is None
nulinspiratie Oct 3, 2017
7f27a6b
add test of paramter issue
jenshnielsen Oct 6, 2017
86d9620
Merge remote-tracking branch 'nulinspiratie/hotfix/parameter_scale_ra…
jenshnielsen Oct 6, 2017
95c058d
Add tests cleanup
jenshnielsen Oct 9, 2017
7b93a14
try to write parameter code a bit cleaner
jenshnielsen Oct 9, 2017
b6582fb
Merge remote-tracking branch 'origin/master' into fix_parameters
jenshnielsen Oct 9, 2017
26288c6
annotate parameters
jenshnielsen Oct 9, 2017
30c00e0
fix typechecking
jenshnielsen Oct 9, 2017
61842ed
set private variable in init
jenshnielsen Oct 9, 2017
0e3d55c
Rename unwrapped get to get_raw to avoid confusion
jenshnielsen Oct 9, 2017
e216cee
transform to step size before mapping, scaling and parsing@
jenshnielsen Oct 9, 2017
6835565
one ome deprecate ManualParameter
jenshnielsen Oct 9, 2017
88c6545
Merge branch 'master' into fix_parameters
jenshnielsen Oct 12, 2017
0486adf
revert setting parameter via _private value
jenshnielsen Oct 12, 2017
7e5df57
Try to wrap get/set_raw first in baseclass
jenshnielsen Oct 12, 2017
eabdd6e
Merge branch 'master' into fix_parameters
jenshnielsen Oct 13, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 93 additions & 47 deletions qcodes/instrument/parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import os
import collections
import warnings
from typing import Optional, Sequence, TYPE_CHECKING, Union, Callable
from functools import partial, wraps
import numpy

Expand All @@ -70,6 +71,9 @@
from qcodes.instrument.sweep_values import SweepFixedValues
from qcodes.data.data_array import DataArray

if TYPE_CHECKING:
from .base import Instrument


class _BaseParameter(Metadatable, DeferredOperations):
"""
Expand Down Expand Up @@ -144,10 +148,20 @@ class _BaseParameter(Metadatable, DeferredOperations):
JSON snapshot of the parameter
"""

def __init__(self, name, instrument, snapshot_get=True, metadata=None,
step=None, scale=None, inter_delay=0, post_delay=0,
val_mapping=None, get_parser=None, set_parser=None,
snapshot_value=True, max_val_age=None, vals=None):
def __init__(self, name: str,
instrument: Optional['Instrument'],
snapshot_get: bool=True,
metadata: Optional[dict]=None,
step: Optional[Union[int, float]]=None,
scale: Optional[Union[int, float]]=None,
inter_delay: Union[int, float]=0,
post_delay: Union[int, float]=0,
val_mapping: Optional[dict]=None,
get_parser: Optional[Callable]=None,
set_parser: Optional[Callable]=None,
snapshot_value: bool=True,
max_val_age: Optional[float]=None,
vals: Optional[Validator]=None):
super().__init__(metadata)
self.name = str(name)
self._instrument = instrument
Expand All @@ -160,11 +174,11 @@ def __init__(self, name, instrument, snapshot_get=True, metadata=None,
vals = Enum(*val_mapping.keys())
self.vals = vals

self.step = step
self._step = step
self.scale = scale
self.raw_value = None
self.inter_delay = inter_delay
self.post_delay = post_delay
self._inter_delay = inter_delay
self._post_delay = post_delay

self.val_mapping = val_mapping
if val_mapping is None:
Expand All @@ -178,7 +192,7 @@ def __init__(self, name, instrument, snapshot_get=True, metadata=None,
# record of latest value and when it was set or measured
# what exactly this means is different for different subclasses
# but they all use the same attributes so snapshot is consistent.
self._latest = {'value': None, 'ts': None}
self._latest = {'value': None, 'ts': None, 'raw_value': None}
self.get_latest = GetLatest(self, max_val_age=max_val_age)

if hasattr(self, 'get'):
Expand All @@ -188,7 +202,7 @@ def __init__(self, name, instrument, snapshot_get=True, metadata=None,

# subclasses should extend this list with extra attributes they
# want automatically included in the snapshot
self._meta_attrs = ['name', 'instrument', 'step', 'scale', 'raw_value',
self._meta_attrs = ['name', 'instrument', 'step', 'scale',
'inter_delay', 'post_delay', 'val_mapping', 'vals']

# Specify time of last set operation, used when comparing to delay to
Expand Down Expand Up @@ -220,14 +234,16 @@ def __call__(self, *args, **kwargs):
raise NotImplementedError('no set cmd found in' +
' Parameter {}'.format(self.name))

def snapshot_base(self, update=False):
def snapshot_base(self, update: bool=False,
params_to_skip_update: Sequence[str]=None) -> dict:
"""
State of the parameter as a JSON-compatible dict.

Args:
update (bool): If True, update the state by calling
parameter.get().
If False, just use the latest values in memory.
params_to_skip_update: No effect but may be passed from super Class:

Returns:
dict: base snapshot
Expand All @@ -243,6 +259,7 @@ def snapshot_base(self, update=False):

if not self._snapshot_value:
state.pop('value')
state.pop('raw_value', None)

if isinstance(state['ts'], datetime):
state['ts'] = state['ts'].strftime('%Y-%m-%d %H:%M:%S')
Expand All @@ -265,9 +282,18 @@ def snapshot_base(self, update=False):
return state

def _save_val(self, value, validate=False):
"""
Update latest
"""
if validate:
self.validate(value)
self._latest = {'value': value, 'ts': datetime.now()}
if (self.get_parser is None and
self.set_parser is None and
self.val_mapping is None and
self.scale is None):
self.raw_value = value
self._latest = {'value': value, 'ts': datetime.now(),
'raw_value': self.raw_value}

def _wrap_get(self, get_function):
@wraps(get_function)
Expand Down Expand Up @@ -295,11 +321,11 @@ def get_wrapper(*args, **kwargs):
if self.val_mapping is not None:
if value in self.inverse_val_mapping:
value = self.inverse_val_mapping[value]
elif int(value) in self.inverse_val_mapping:
value = self.inverse_val_mapping[int(value)]
else:
raise KeyError("'{}' not in val_mapping".format(value))

try:
value = self.inverse_val_mapping[int(value)]
except (ValueError, KeyError):
raise KeyError("'{}' not in val_mapping".format(value))
self._save_val(value)
return value
except Exception as e:
Expand All @@ -314,26 +340,33 @@ def set_wrapper(value, **kwargs):
try:
self.validate(value)

if self.val_mapping is not None:
# Convert set values using val_mapping dictionary
value = self.val_mapping[value]

if self.scale is not None:
if isinstance(self.scale, collections.Iterable):
# Scale contains multiple elements, one for each value
value = tuple(val * scale for val, scale
in zip(value, self.scale))
else:
# Use single scale for all values
value *= self.scale

if self.set_parser is not None:
value = self.set_parser(value)

# In some cases intermediate sweep values must be used.
# Unless `self.step` is defined, get_sweep_values will return
# a list containing only `value`.
for val in self.get_ramp_values(value, step=self.step):
steps = self.get_ramp_values(value, step=self.step)

for val_step in steps:
if self.val_mapping is not None:
# Convert set values using val_mapping dictionary
mapped_value = self.val_mapping[val_step]
else:
mapped_value = val_step

if self.scale is not None:
if isinstance(self.scale, collections.Iterable):
# Scale contains multiple elements, one for each value
scaled_mapped_value = tuple(val * scale for val, scale
in zip(mapped_value, self.scale))
else:
# Use single scale for all values
scaled_mapped_value = mapped_value*self.scale
else:
scaled_mapped_value = mapped_value

if self.set_parser is not None:
parsed_scaled_mapped_value = self.set_parser(scaled_mapped_value)
else:
parsed_scaled_mapped_value = scaled_mapped_value

# Check if delay between set operations is required
t_elapsed = time.perf_counter() - self._t_last_set
Expand All @@ -345,10 +378,11 @@ def set_wrapper(value, **kwargs):
# Start timer to measure execution time of set_function
t0 = time.perf_counter()

set_function(val, **kwargs)
self.raw_value = val
self._save_val(val, validate=(self.val_mapping is None and
self.set_parser is None))
set_function(parsed_scaled_mapped_value, **kwargs)
self.raw_value = parsed_scaled_mapped_value
self._save_val(val_step,
validate=(self.val_mapping is None and
self.set_parser is None))

# Update last set time (used for calculating delays)
self._t_last_set = time.perf_counter()
Expand Down Expand Up @@ -379,7 +413,11 @@ def get_ramp_values(self, value, step=None):
if step is None:
return [value]
else:
start_value = self.get_latest()
if isinstance(value, collections.Iterable) and len(value) > 1:
raise RuntimeError("Don't know how to step a parameter with more than one value")
if self.get_latest() is None:
self.get()
start_value = self.raw_value

self.validate(start_value)

Expand Down Expand Up @@ -638,9 +676,17 @@ class Parameter(_BaseParameter):

"""

def __init__(self, name, instrument=None, label=None, unit=None,
get_cmd=None, set_cmd=False, initial_value=None,
max_val_age=None, vals=None, docstring=None, **kwargs):
def __init__(self, name: str,
instrument: Optional['Instrument']=None,
label: Optional[str]=None,
unit: Optional[str]=None,
get_cmd: Optional[Union[str, Callable, bool]]=None,
set_cmd: Optional[Union[str, Callable, bool]]=False,
initial_value: Optional[Union[float, int, str]]=None,
max_val_age: Optional[float]=None,
vals: Optional[str]=None,
docstring: Optional[str]=None,
**kwargs):
super().__init__(name=name, instrument=instrument, vals=vals, **kwargs)

# Enable set/get methods if get_cmd/set_cmd is given
Expand All @@ -650,27 +696,27 @@ def __init__(self, name, instrument=None, label=None, unit=None,
if max_val_age is not None:
raise SyntaxError('Must have get method or specify get_cmd '
'when max_val_age is set')
self.get = self.get_latest
self.get_raw = lambda: self._latest['raw_value']
else:
exec_str = instrument.ask if instrument else None
self.get = Command(arg_count=0, cmd=get_cmd, exec_str=exec_str)
self.get = self._wrap_get(self.get)
self.get_raw = Command(arg_count=0, cmd=get_cmd, exec_str=exec_str)
self.get = self._wrap_get(self.get_raw)

if not hasattr(self, 'set') and set_cmd is not False:
if set_cmd is None:
self.set = partial(self._save_val, validate=False)
self.set_raw = partial(self._save_val, validate=False)
else:
exec_str = instrument.write if instrument else None
self.set = Command(arg_count=1, cmd=set_cmd, exec_str=exec_str)
self.set = self._wrap_set(self.set)
self.set_raw = Command(arg_count=1, cmd=set_cmd, exec_str=exec_str)
self.set = self._wrap_set(self.set_raw)

self._meta_attrs.extend(['label', 'unit', 'vals'])

self.label = name if label is None else label
self.unit = unit if unit is not None else ''

if initial_value is not None:
self._save_val(initial_value, validate=True)
self.set(initial_value)

# generate default docstring
self.__doc__ = os.linesep.join((
Expand Down
7 changes: 4 additions & 3 deletions qcodes/measure.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from datetime import datetime

from qcodes.instrument.parameter import ManualParameter
from qcodes.instrument.parameter import Parameter
from qcodes.loops import Loop
from qcodes.actions import _actions_snapshot
from qcodes.utils.helpers import full_class
Expand All @@ -18,8 +18,9 @@ class Measure(Metadatable):
Scalars returned by an action will be saved as length-1 arrays,
with a dummy setpoint for consistency with other DataSets.
"""
dummy_parameter = ManualParameter(name='single',
label='Single Measurement')
dummy_parameter = Parameter(name='single',
label='Single Measurement',
set_cmd=None, get_cmd=None)

def __init__(self, *actions):
super().__init__()
Expand Down
38 changes: 32 additions & 6 deletions qcodes/tests/test_parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -622,14 +622,40 @@ def test_val_mapping_with_parsers(self):
self._p = 'PVAL: 1'
self.assertEqual(p(), 'on')

class TestManualParameterValMapping(TestCase):
def setUp(self):
self.instrument = DummyInstrument('dummy_holder')

def tearDown(self):
self.instrument.close()
del self.instrument


def test_val_mapping(self):
self.instrument.add_parameter('myparameter', set_cmd=None, get_cmd=None, val_mapping={'A': 0, 'B': 1})
self.instrument.myparameter('A')
assert self.instrument.myparameter() == 'A'
assert self.instrument.myparameter() == 'A'
assert self.instrument.myparameter.raw_value == 0



class TestInstrumentRefParameter(TestCase):

def setUp(self):
self.a = DummyInstrument('dummy_holder')
self.d = DummyInstrument('dummy')

def test_get_instr(self):
a = DummyInstrument('dummy_holder')
d = DummyInstrument('dummy')
a.add_parameter('test', parameter_class=InstrumentRefParameter)
self.a.add_parameter('test', parameter_class=InstrumentRefParameter)

self.a.test.set(self.d.name)

a.test.set(d.name)
self.assertEqual(self.a.test.get(), self.d.name)
self.assertEqual(self.a.test.get_instr(), self.d)

self.assertEqual(a.test.get(), d.name)
self.assertEqual(a.test.get_instr(), d)
def tearDown(self):
self.a.close()
self.d.close()
del self.a
del self.d