diff --git a/docs/device_object.rst b/docs/device_object.rst index 65fda83b..d5d5cc0c 100644 --- a/docs/device_object.rst +++ b/docs/device_object.rst @@ -143,8 +143,11 @@ in the function call of measurement scripts, you can set the argument priorize_s Another important feature is the possibility to save and load device working points. To store a certain configuration as your default working point, use device.save_defaults. This stores all parameter values (of parameters that can be set). With device.set_defaults() you can reset it to the stored configuration. Alternatively you can use "device.save_state(name)" and "device.set_state(name)" to store and set multiple working points with -custom names. They can also be accessed via "device.states" in case you forgot the name. Be aware that the set commands currently set the -parameters instead of ramping to them, which can endanger your device if it is sensitive to voltage jumps. +custom names. They can also be accessed via "device.states" in case you forgot the name. +For all of those methods the parameters are ramped to the final state by default (with the default QuMada ramp rate). You can use the argument "ramp = False" to avoid this, or use keyword +arguments (ramp_rate, duration) to adjust the ramp speed when calling the methods. Alternatively, you can set the parameter "device.ramp" to True or False in order to control the behaviour +for the complete device. + ############### diff --git a/src/qumada/instrument/custom_drivers/ZI/MFLI.py b/src/qumada/instrument/custom_drivers/ZI/MFLI.py index 5374bf5a..afb371f4 100644 --- a/src/qumada/instrument/custom_drivers/ZI/MFLI.py +++ b/src/qumada/instrument/custom_drivers/ZI/MFLI.py @@ -84,7 +84,7 @@ def __init__( unit="V", get_cmd=lambda: demod0.sample()["y"], get_parser=float, - set_cmd=None, + set_cmd=False, docstring="X component of sample measured by demod1", ) self.voltage_y_component.signal_name = ("demod0", "y") @@ -95,7 +95,7 @@ def __init__( unit="V", get_cmd=lambda: demod0.sample()["y"], get_parser=float, - set_cmd=None, + set_cmd=False, docstring="X component of sample measured by demod1", ) self.voltage_x_component.signal_name = ("demod0", "x") @@ -106,7 +106,7 @@ def __init__( unit="A", get_cmd=lambda: np.sqrt(demod0.sample()["x"] ** 2 + demod0.sample()["y"] ** 2), get_parser=float, - set_cmd=None, + set_cmd=False, docstring="Absolute current as measured by demod0", ) self.current.signal_name = ("demod0", "r") @@ -116,7 +116,7 @@ def __init__( unit="A", get_cmd=lambda: demod0.sample()["x"], get_parser=float, - set_cmd=None, + set_cmd=False, docstring="X component of sample measured by demod1", ) self.current_x_component.signal_name = ("demod0", "x") @@ -127,7 +127,7 @@ def __init__( unit="rad", get_cmd=lambda: demod0.sample()["phase"], get_parser=float, - set_cmd=None, + set_cmd=False, docstring="Phase of the measured current in radians", ) self.phase.signal_name = ("demod0", "phase") @@ -137,7 +137,7 @@ def __init__( unit="A", get_cmd=lambda: demod0.sample()["y"], get_parser=float, - set_cmd=None, + set_cmd=False, docstring="X component of sample measured by demod1", ) self.current_y_component.signal_name = ("demod0", "y") @@ -205,7 +205,7 @@ def __init__( name="demod0_aux_in_1", label="Demod0 AuxIn 1", get_cmd=lambda: demod0.sample()["auxin0"], - set_cmd=None, + set_cmd=False, get_parser=float, docstring="Aux In 1 of demod0", ) @@ -215,7 +215,7 @@ def __init__( name="demod0_aux_in_2", label="Demod0 AuxIn 2", get_cmd=lambda: demod0.sample()["auxin1"], - set_cmd=None, + set_cmd=False, get_parser=float, docstring="Aux In 2 of demod0", ) diff --git a/src/qumada/measurement/device_object.py b/src/qumada/measurement/device_object.py index 8c747b5c..e62f77b5 100644 --- a/src/qumada/measurement/device_object.py +++ b/src/qumada/measurement/device_object.py @@ -49,6 +49,7 @@ def __init__( self.station = station self.buffer_script_setup = {} self.states = {} + self.ramp: bool = True def add_terminal(self, terminal_name: str, type: str | None = None, terminal_data: dict | None = {}): if terminal_name not in self.terminals.keys(): @@ -82,7 +83,7 @@ def update_terminal_parameters(self): for param in mapping.keys(): self.terminals[terminal].update_terminal_parameter(param) - def save_defaults(self): + def save_defaults(self, ramp=None, **kwargs): """ Saves current values as default for all Terminals and their parameters """ @@ -96,22 +97,28 @@ def save_state(self, name: str): """ self.states[name] = self.save_to_dict(priorize_stored_value=False) - def set_state(self, name: str): + def set_state(self, name: str, ramp=None, **kwargs): + if ramp is None: + ramp = self.ramp self.load_from_dict(self.states[name]) - self.set_stored_values() + self.set_stored_values(ramp=ramp, **kwargs) - def set_stored_values(self): + def set_stored_values(self, ramp=None, **kwargs): + if ramp is None: + ramp = self.ramp for terminal in self.terminals.values(): for param in terminal.terminal_parameters.values(): param.set_stored_value() - def set_defaults(self): + def set_defaults(self, ramp=None, **kwargs): """ Sets all Terminals and their parameters to their default values """ + if ramp is None: + ramp = self.ramp for terminal in self.terminals.values(): for param in terminal.terminal_parameters.values(): - param.set_default() + param.set_default(ramp=ramp, **kwargs) def voltages(self): """ @@ -515,7 +522,7 @@ def value(self): @value.setter def value(self, value): - if self.locked: + if self.locked is True: raise Exception(f"Parameter {self.name} of Terminal {self._parent.name} is locked and cannot be set!") return @@ -679,31 +686,37 @@ def save_default(self): logger.warning(f"{e} was raised when trying to save default value of {self.name}") pass - def set_default(self): + def set_default(self, ramp=True, **kwargs): """ Sets value to default value """ if self.default_value is not None: try: - self.value = self.default_value + if ramp is True: + self.ramp(self.default_value, **kwargs) + else: + self.value = self.default_value except NotImplementedError as e: logger.debug(f"{e} was raised and ignored") else: logger.warning(f"No default value set for parameter {self.name}") - def set_stored_value(self): + def set_stored_value(self, ramp=True, **kwargs): """ Sets value to stored value from dict """ if self._stored_value is not None: try: - self.value = self._stored_value + if ramp is True: + self.ramp(self._stored_value, **kwargs) + else: + self.value = self._stored_value except NotImplementedError as e: logger.debug(f"{e} was raised and ignored") else: logger.warning(f"No stored value set for parameter {self.name}") - def __call__(self, value=None): + def __call__(self, value=None, ramp=None): if value is None: return self.value else: diff --git a/src/qumada/utils/ramp_parameter.py b/src/qumada/utils/ramp_parameter.py index eb89dc57..834b4f68 100644 --- a/src/qumada/utils/ramp_parameter.py +++ b/src/qumada/utils/ramp_parameter.py @@ -24,6 +24,7 @@ import logging import time +from math import isclose from qumada.utils.generate_sweeps import generate_sweep @@ -41,6 +42,7 @@ def ramp_parameter( ramp_time: float | None = None, setpoint_intervall: float = 0.1, valid_units: str = "all", + tolerance: float = 1e-5, **kwargs, ): """ @@ -68,6 +70,9 @@ def ramp_parameter( The default is 0.1. valid_units : str, optional Not used yet. The default is "all". + tolerance: float, optional + If abs(current_value- target_value) < tolerance*max(current_value, target_value) + no ramp is done. Default 1e-5. **kwargs : TYPE DESCRIPTION. @@ -82,13 +87,21 @@ def ramp_parameter( True if sweep was completed, False if it failed. """ - # time.sleep(0.1) - LOG.debug(f"parameter: {parameter}") + if parameter._settable is False: + LOG.warning(f"{parameter} is not _settable and cannot be ramped!") + return False current_value = parameter.get() + LOG.debug(f"parameter: {parameter}") LOG.debug(f"current value: {current_value}") + LOG.debug(f"ramp rate: {ramp_rate}") + LOG.debug(f"ramp time: {ramp_time}") if isinstance(current_value, float): LOG.debug(f"target: {target}") + if isclose(current_value, target, rel_tol=tolerance): + LOG.debug("Target value is sufficiently close to current_value, no need to ramp") + return True + if not ramp_rate: if not ramp_time: print("Please specify either ramp_time or ramp_speed") @@ -99,7 +112,7 @@ def ramp_parameter( num_points = int(abs(current_value - float(target)) / (ramp_rate * setpoint_intervall)) + 2 if ramp_time is not None and ramp_time < abs(current_value - float(target)) / ramp_rate: print( - "Ramp rate is to low to reach target value in specified" + f"Ramp rate of {param} is to low to reach target value in specified" "max ramp time. Adapting ramp rate to match ramp time" ) return ramp_parameter(