From 697f7ec28bc30e4ec5ab8fdc69258a20a200771b Mon Sep 17 00:00:00 2001 From: Nikolaos Chatzaras <39853363+SnX59@users.noreply.github.com> Date: Mon, 21 Sep 2026 13:47:42 +0200 Subject: [PATCH 1/7] Modify Lakeshore Model 336 heater setup parameters Updated the Lakeshore Model 336 driver to exclude output-type field for heater setup and disable heater setup for unpowered outputs. --- .../Lakeshore/Lakeshore_model_336.py | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/qcodes/instrument_drivers/Lakeshore/Lakeshore_model_336.py b/src/qcodes/instrument_drivers/Lakeshore/Lakeshore_model_336.py index 3af71f9ab98f..e9e3a64284ea 100644 --- a/src/qcodes/instrument_drivers/Lakeshore/Lakeshore_model_336.py +++ b/src/qcodes/instrument_drivers/Lakeshore/Lakeshore_model_336.py @@ -59,7 +59,16 @@ def __init__( output_index: int, **kwargs: "Unpack[InstrumentBaseKWArgs]", ): - super().__init__(parent, output_name, output_index, has_pid=True, **kwargs) + # The Model 336 HTRSET command has no output-type field (unlike the + # Model 335), so exclude ``output_type`` from the heater setup group. + super().__init__( + parent, + output_name, + output_index, + has_pid=True, + heater_output_has_output_type=False, + **kwargs, + ) self.P.vals = vals.Numbers(0.1, 1000) self.I.vals = vals.Numbers(0.1, 1000) @@ -96,7 +105,16 @@ def __init__( output_index: int, **kwargs: "Unpack[InstrumentBaseKWArgs]", ): - super().__init__(parent, output_name, output_index, has_pid=False, **kwargs) + # Outputs 3 and 4 are unpowered analog voltage outputs that do not + # support the heater setup (HTRSET) command, so disable it entirely. + super().__init__( + parent, + output_name, + output_index, + has_pid=False, + has_heater_output_setup=False, + **kwargs, + ) class LakeshoreModel336Channel(LakeshoreBaseSensorChannel): From 6fc9decfd6f607a06b5825e19711229e1b35e65e Mon Sep 17 00:00:00 2001 From: Nikolaos Chatzaras <39853363+SnX59@users.noreply.github.com> Date: Mon, 21 Sep 2026 13:48:24 +0200 Subject: [PATCH 2/7] Add heater output setup parameters to Lakeshore base Added parameters for heater output setup including output type, heater resistance, maximum current, and display settings. Enhanced functionality for heater output configuration. --- .../Lakeshore/lakeshore_base.py | 136 +++++++++++------- 1 file changed, 85 insertions(+), 51 deletions(-) diff --git a/src/qcodes/instrument_drivers/Lakeshore/lakeshore_base.py b/src/qcodes/instrument_drivers/Lakeshore/lakeshore_base.py index 1b8b9ff9d8e1..1e138fa515f9 100644 --- a/src/qcodes/instrument_drivers/Lakeshore/lakeshore_base.py +++ b/src/qcodes/instrument_drivers/Lakeshore/lakeshore_base.py @@ -34,6 +34,8 @@ def __init__( output_name: str, output_index: int, has_pid: bool = True, + has_heater_output_setup: bool = True, + heater_output_has_output_type: bool = True, **kwargs: "Unpack[InstrumentBaseKWArgs]", ): """ @@ -46,6 +48,17 @@ def __init__( instrument has_pid: if True, then the output supports closed loop control, hence it will have three parameters to set it up: 'P', 'I', and 'D' + has_heater_output_setup: if True, then the output supports the heater + setup (``HTRSET``) command, hence it will have the parameters to set it + up (``output_heater_resistance``, ``output_max_current``, + ``output_max_user_current``, ``output_display`` and, depending on + ``heater_output_has_output_type``, ``output_type``). Set to False for + outputs that do not support ``HTRSET`` (e.g. the analog voltage + outputs 3 and 4 of the Model 336). + heater_output_has_output_type: if True, the heater setup (``HTRSET``) + command includes the ``output_type`` field. Set to False for models + whose ``HTRSET`` command does not have an output-type field (e.g. the + Model 336). Only relevant when ``has_heater_output_setup`` is True. **kwargs: Forwarded to baseclass. """ @@ -129,61 +142,82 @@ def __init__( get_cmd=f"PID? {output_index}", ) - self.output_type: GroupParameter = self.add_parameter( - name="output_type", - docstring="Output type (Output 2 only): 0=Current, 1=Voltage", - val_mapping=( - {"current": 0, "voltage": 1} if output_index == 1 else {"current": 0} - ), - parameter_class=GroupParameter, - ) - """Output type (Output 2 only): 0=Current, 1=Voltage""" - - self.output_heater_resistance: GroupParameter = self.add_parameter( - name="output_heater_resistance", - docstring="Heater Resistance Setting: 25/50ohm", - val_mapping={"25ohm": 1, "50ohm": 2}, - parameter_class=GroupParameter, - ) - """Heater Resistance Setting: 25/50ohm""" + self._has_heater_output_setup = has_heater_output_setup + if has_heater_output_setup: + heater_setup_params: list[GroupParameter] = [] + heater_setup_set_fields: list[str] = [] + + if heater_output_has_output_type: + self.output_type: GroupParameter = self.add_parameter( + name="output_type", + docstring="Output type (Output 2 only): 0=Current, 1=Voltage", + val_mapping=( + {"current": 0, "voltage": 1} + if output_index == 1 + else {"current": 0} + ), + parameter_class=GroupParameter, + ) + """Output type (Output 2 only): 0=Current, 1=Voltage""" + heater_setup_params.append(self.output_type) + heater_setup_set_fields.append("{output_type}") + + self.output_heater_resistance: GroupParameter = self.add_parameter( + name="output_heater_resistance", + docstring="Heater Resistance Setting: 25/50ohm", + val_mapping={"25ohm": 1, "50ohm": 2}, + parameter_class=GroupParameter, + ) + """Heater Resistance Setting: 25/50ohm""" - self.output_max_current: GroupParameter = self.add_parameter( - name="output_max_current", - docstring="Specifies the maximum heater output current: User Specified, 0.707 A, 1 A, 1.141 A, 1.732", - val_mapping={"user": 0, "0.707A": 1, "1A": 2, "1.141A": 3, "1.732A": 4}, - parameter_class=GroupParameter, - ) - """Specifies the maximum heater output current: User Specified, 0.707 A, 1 A, 1.141 A, 1.732""" + self.output_max_current: GroupParameter = self.add_parameter( + name="output_max_current", + docstring="Specifies the maximum heater output current: User Specified, 0.707 A, 1 A, 1.141 A, 1.732", + val_mapping={"user": 0, "0.707A": 1, "1A": 2, "1.141A": 3, "1.732A": 4}, + parameter_class=GroupParameter, + ) + """Specifies the maximum heater output current: User Specified, 0.707 A, 1 A, 1.141 A, 1.732""" - self.output_max_user_current: GroupParameter = self.add_parameter( - name="output_max_user_current", - docstring="Specifies the maximum heater output current if max current is set to User Specified.", - vals=vals.Numbers(0, 1.732), - unit="A", - get_parser=float, - parameter_class=GroupParameter, - ) - """Specifies the maximum heater output current if max current is set to User Specified.""" + self.output_max_user_current: GroupParameter = self.add_parameter( + name="output_max_user_current", + docstring="Specifies the maximum heater output current if max current is set to User Specified.", + vals=vals.Numbers(0, 1.732), + unit="A", + get_parser=float, + parameter_class=GroupParameter, + ) + """Specifies the maximum heater output current if max current is set to User Specified.""" - self.output_display: GroupParameter = self.add_parameter( - name="output_display", - docstring="Specifies whether the heater output displays in current or power (current mode only)", - val_mapping={"current": 1, "power": 2}, - parameter_class=GroupParameter, - ) - """Specifies whether the heater output displays in current or power (current mode only)""" + self.output_display: GroupParameter = self.add_parameter( + name="output_display", + docstring="Specifies whether the heater output displays in current or power (current mode only)", + val_mapping={"current": 1, "power": 2}, + parameter_class=GroupParameter, + ) + """Specifies whether the heater output displays in current or power (current mode only)""" + + heater_setup_params.extend( + [ + self.output_heater_resistance, + self.output_max_current, + self.output_max_user_current, + self.output_display, + ] + ) + heater_setup_set_fields.extend( + [ + "{output_heater_resistance}", + "{output_max_current}", + "{output_max_user_current}", + "{output_display}", + ] + ) - self.heater_group = Group( - [ - self.output_type, - self.output_heater_resistance, - self.output_max_current, - self.output_max_user_current, - self.output_display, - ], - set_cmd=f"HTRSET {output_index},{{output_type}},{{output_heater_resistance}},{{output_max_current}},{{output_max_user_current}},{{output_display}}", - get_cmd=f"HTRSET? {output_index}", - ) + self.heater_group = Group( + heater_setup_params, + set_cmd=f"HTRSET {output_index},{','.join(heater_setup_set_fields)}", + get_cmd=f"HTRSET? {output_index}", + ) self.output_range: Parameter = self.add_parameter( "output_range", From 50c4d18d804a9132a04f4064dd65b50ef03f74cf Mon Sep 17 00:00:00 2001 From: Nikolaos Chatzaras <39853363+SnX59@users.noreply.github.com> Date: Mon, 21 Sep 2026 14:00:29 +0200 Subject: [PATCH 3/7] Update default values for htrset_output_1 and 2 --- src/qcodes/instrument/sims/lakeshore_model336.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qcodes/instrument/sims/lakeshore_model336.yaml b/src/qcodes/instrument/sims/lakeshore_model336.yaml index fbeca651a23c..837249624d7e 100644 --- a/src/qcodes/instrument/sims/lakeshore_model336.yaml +++ b/src/qcodes/instrument/sims/lakeshore_model336.yaml @@ -301,7 +301,7 @@ devices: r: "{}" htrset_output_1: - default: "1, 5" + default: "2,2,+1.7320E+00,1" getter: q: "HTRSET? 1" r: "{}" @@ -309,7 +309,7 @@ devices: q: "HTRSET 1, {}" htrset_output_2: - default: "1, 5" + default: "2,2,+1.7320E+00,1" getter: q: "HTRSET? 2" r: "{}" From 91f360dd1d9681bc1fee8bb2eedca6e3471365d6 Mon Sep 17 00:00:00 2001 From: Nikolaos Chatzaras <39853363+SnX59@users.noreply.github.com> Date: Fri, 2 Oct 2026 10:12:39 +0200 Subject: [PATCH 4/7] Move heater setup to output subclasses - to all Lakeshore_drivers Move Lakeshore heater setup to output subclasses and also extended the fix to the 335 and 372 models. --- .../Lakeshore/Lakeshore_model_336.py | 30 +-- .../Lakeshore/Lakeshore_model_372.py | 4 +- .../Lakeshore/_lakeshore_model_335.py | 4 +- .../Lakeshore/lakeshore_base.py | 223 +++++++++++------- 4 files changed, 144 insertions(+), 117 deletions(-) diff --git a/src/qcodes/instrument_drivers/Lakeshore/Lakeshore_model_336.py b/src/qcodes/instrument_drivers/Lakeshore/Lakeshore_model_336.py index e9e3a64284ea..44b5929a9651 100644 --- a/src/qcodes/instrument_drivers/Lakeshore/Lakeshore_model_336.py +++ b/src/qcodes/instrument_drivers/Lakeshore/Lakeshore_model_336.py @@ -6,6 +6,7 @@ from .lakeshore_base import ( LakeshoreBase, LakeshoreBaseOutput, + LakeshoreBaseOutputWithHeaterSetup, LakeshoreBaseSensorChannel, ) @@ -31,12 +32,13 @@ } -class LakeshoreModel336CurrentSource(LakeshoreBaseOutput): +class LakeshoreModel336CurrentSource(LakeshoreBaseOutputWithHeaterSetup): """ InstrumentChannel for current sources on Lakeshore Model 336. Class for control outputs 1 and 2 of Lakeshore Model 336 that are variable DC current - sources referenced to chassis ground. + sources referenced to chassis ground. Unlike the Model 335, the heater setup + (``HTRSET``) command of the Model 336 has no output type field. """ MODES: ClassVar[dict[str, int]] = { @@ -59,16 +61,7 @@ def __init__( output_index: int, **kwargs: "Unpack[InstrumentBaseKWArgs]", ): - # The Model 336 HTRSET command has no output-type field (unlike the - # Model 335), so exclude ``output_type`` from the heater setup group. - super().__init__( - parent, - output_name, - output_index, - has_pid=True, - heater_output_has_output_type=False, - **kwargs, - ) + super().__init__(parent, output_name, output_index, has_pid=True, **kwargs) self.P.vals = vals.Numbers(0.1, 1000) self.I.vals = vals.Numbers(0.1, 1000) @@ -80,7 +73,7 @@ class LakeshoreModel336VoltageSource(LakeshoreBaseOutput): InstrumentChannel for voltage sources on Lakeshore Model 336. This is used for control outputs 3 and 4 that are variable DC voltage - sources. + sources. These outputs do not support the heater setup (``HTRSET``) command. """ MODES: ClassVar[dict[str, int]] = { @@ -105,16 +98,7 @@ def __init__( output_index: int, **kwargs: "Unpack[InstrumentBaseKWArgs]", ): - # Outputs 3 and 4 are unpowered analog voltage outputs that do not - # support the heater setup (HTRSET) command, so disable it entirely. - super().__init__( - parent, - output_name, - output_index, - has_pid=False, - has_heater_output_setup=False, - **kwargs, - ) + super().__init__(parent, output_name, output_index, has_pid=False, **kwargs) class LakeshoreModel336Channel(LakeshoreBaseSensorChannel): diff --git a/src/qcodes/instrument_drivers/Lakeshore/Lakeshore_model_372.py b/src/qcodes/instrument_drivers/Lakeshore/Lakeshore_model_372.py index 22ac6f1713de..0596f8797ab0 100644 --- a/src/qcodes/instrument_drivers/Lakeshore/Lakeshore_model_372.py +++ b/src/qcodes/instrument_drivers/Lakeshore/Lakeshore_model_372.py @@ -3,7 +3,7 @@ import qcodes.validators as vals from qcodes.instrument_drivers.Lakeshore.lakeshore_base import ( LakeshoreBase, - LakeshoreBaseOutput, + LakeshoreBaseOutputWithHeaterSetupAndOutputType, LakeshoreBaseSensorChannel, ) from qcodes.parameters import Group, GroupParameter @@ -17,7 +17,7 @@ _n_channels = 16 -class LakeshoreModel372Output(LakeshoreBaseOutput): +class LakeshoreModel372Output(LakeshoreBaseOutputWithHeaterSetupAndOutputType): """An InstrumentChannel for control outputs (heaters) of Lakeshore Model 372""" MODES: ClassVar[dict[str, int]] = { diff --git a/src/qcodes/instrument_drivers/Lakeshore/_lakeshore_model_335.py b/src/qcodes/instrument_drivers/Lakeshore/_lakeshore_model_335.py index afa82d4ababc..ee5092169ef2 100644 --- a/src/qcodes/instrument_drivers/Lakeshore/_lakeshore_model_335.py +++ b/src/qcodes/instrument_drivers/Lakeshore/_lakeshore_model_335.py @@ -8,7 +8,7 @@ from .lakeshore_base import ( LakeshoreBase, - LakeshoreBaseOutput, + LakeshoreBaseOutputWithHeaterSetupAndOutputType, LakeshoreBaseSensorChannel, ) @@ -147,7 +147,7 @@ def __init__( ) -class LakeshoreModel335CurrentSource(LakeshoreBaseOutput): +class LakeshoreModel335CurrentSource(LakeshoreBaseOutputWithHeaterSetupAndOutputType): """ InstrumentChannel for current sources on Lakeshore Model 335. diff --git a/src/qcodes/instrument_drivers/Lakeshore/lakeshore_base.py b/src/qcodes/instrument_drivers/Lakeshore/lakeshore_base.py index 1e138fa515f9..b07ced780f81 100644 --- a/src/qcodes/instrument_drivers/Lakeshore/lakeshore_base.py +++ b/src/qcodes/instrument_drivers/Lakeshore/lakeshore_base.py @@ -34,13 +34,17 @@ def __init__( output_name: str, output_index: int, has_pid: bool = True, - has_heater_output_setup: bool = True, - heater_output_has_output_type: bool = True, **kwargs: "Unpack[InstrumentBaseKWArgs]", ): """ Base class for the outputs of Lakeshore temperature controllers + This class does not add the parameters of the heater setup + (``HTRSET``) command because not all outputs support it and its + format differs between models. Outputs that support it should + subclass :class:`LakeshoreBaseOutputWithHeaterSetup` or + :class:`LakeshoreBaseOutputWithHeaterSetupAndOutputType` instead. + Args: parent: instrument that this channel belongs to output_name: name of this output @@ -48,17 +52,6 @@ def __init__( instrument has_pid: if True, then the output supports closed loop control, hence it will have three parameters to set it up: 'P', 'I', and 'D' - has_heater_output_setup: if True, then the output supports the heater - setup (``HTRSET``) command, hence it will have the parameters to set it - up (``output_heater_resistance``, ``output_max_current``, - ``output_max_user_current``, ``output_display`` and, depending on - ``heater_output_has_output_type``, ``output_type``). Set to False for - outputs that do not support ``HTRSET`` (e.g. the analog voltage - outputs 3 and 4 of the Model 336). - heater_output_has_output_type: if True, the heater setup (``HTRSET``) - command includes the ``output_type`` field. Set to False for models - whose ``HTRSET`` command does not have an output-type field (e.g. the - Model 336). Only relevant when ``has_heater_output_setup`` is True. **kwargs: Forwarded to baseclass. """ @@ -142,83 +135,6 @@ def __init__( get_cmd=f"PID? {output_index}", ) - self._has_heater_output_setup = has_heater_output_setup - if has_heater_output_setup: - heater_setup_params: list[GroupParameter] = [] - heater_setup_set_fields: list[str] = [] - - if heater_output_has_output_type: - self.output_type: GroupParameter = self.add_parameter( - name="output_type", - docstring="Output type (Output 2 only): 0=Current, 1=Voltage", - val_mapping=( - {"current": 0, "voltage": 1} - if output_index == 1 - else {"current": 0} - ), - parameter_class=GroupParameter, - ) - """Output type (Output 2 only): 0=Current, 1=Voltage""" - heater_setup_params.append(self.output_type) - heater_setup_set_fields.append("{output_type}") - - self.output_heater_resistance: GroupParameter = self.add_parameter( - name="output_heater_resistance", - docstring="Heater Resistance Setting: 25/50ohm", - val_mapping={"25ohm": 1, "50ohm": 2}, - parameter_class=GroupParameter, - ) - """Heater Resistance Setting: 25/50ohm""" - - self.output_max_current: GroupParameter = self.add_parameter( - name="output_max_current", - docstring="Specifies the maximum heater output current: User Specified, 0.707 A, 1 A, 1.141 A, 1.732", - val_mapping={"user": 0, "0.707A": 1, "1A": 2, "1.141A": 3, "1.732A": 4}, - parameter_class=GroupParameter, - ) - """Specifies the maximum heater output current: User Specified, 0.707 A, 1 A, 1.141 A, 1.732""" - - self.output_max_user_current: GroupParameter = self.add_parameter( - name="output_max_user_current", - docstring="Specifies the maximum heater output current if max current is set to User Specified.", - vals=vals.Numbers(0, 1.732), - unit="A", - get_parser=float, - parameter_class=GroupParameter, - ) - """Specifies the maximum heater output current if max current is set to User Specified.""" - - self.output_display: GroupParameter = self.add_parameter( - name="output_display", - docstring="Specifies whether the heater output displays in current or power (current mode only)", - val_mapping={"current": 1, "power": 2}, - parameter_class=GroupParameter, - ) - """Specifies whether the heater output displays in current or power (current mode only)""" - - heater_setup_params.extend( - [ - self.output_heater_resistance, - self.output_max_current, - self.output_max_user_current, - self.output_display, - ] - ) - heater_setup_set_fields.extend( - [ - "{output_heater_resistance}", - "{output_max_current}", - "{output_max_user_current}", - "{output_display}", - ] - ) - - self.heater_group = Group( - heater_setup_params, - set_cmd=f"HTRSET {output_index},{','.join(heater_setup_set_fields)}", - get_cmd=f"HTRSET? {output_index}", - ) - self.output_range: Parameter = self.add_parameter( "output_range", label="Heater range", @@ -542,6 +458,133 @@ def wait_until_set_point_reached( time.sleep(wait_cycle_time) +class LakeshoreBaseOutputWithHeaterSetup(LakeshoreBaseOutput): + def __init__( + self, + parent: "LakeshoreBase", + output_name: str, + output_index: int, + has_pid: bool = True, + **kwargs: "Unpack[InstrumentBaseKWArgs]", + ): + """ + Base class for the outputs of Lakeshore temperature controllers that + support the heater setup command in the form + ``HTRSET ,,,,`` + (e.g. outputs 1 and 2 of the Model 336). + + Args: + parent: instrument that this channel belongs to + output_name: name of this output + output_index: identifier for this output that is used in VISA commands of the + instrument + has_pid: if True, then the output supports closed loop control, + hence it will have three parameters to set it up: 'P', 'I', and 'D' + **kwargs: Forwarded to baseclass. + + """ + super().__init__(parent, output_name, output_index, has_pid=has_pid, **kwargs) + + self.output_heater_resistance: GroupParameter = self.add_parameter( + name="output_heater_resistance", + docstring="Heater Resistance Setting: 25/50ohm", + val_mapping={"25ohm": 1, "50ohm": 2}, + parameter_class=GroupParameter, + ) + """Heater Resistance Setting: 25/50ohm""" + + self.output_max_current: GroupParameter = self.add_parameter( + name="output_max_current", + docstring="Specifies the maximum heater output current: User Specified, 0.707 A, 1 A, 1.141 A, 1.732", + val_mapping={"user": 0, "0.707A": 1, "1A": 2, "1.141A": 3, "1.732A": 4}, + parameter_class=GroupParameter, + ) + """Specifies the maximum heater output current: User Specified, 0.707 A, 1 A, 1.141 A, 1.732""" + + self.output_max_user_current: GroupParameter = self.add_parameter( + name="output_max_user_current", + docstring="Specifies the maximum heater output current if max current is set to User Specified.", + vals=vals.Numbers(0, 1.732), + unit="A", + get_parser=float, + parameter_class=GroupParameter, + ) + """Specifies the maximum heater output current if max current is set to User Specified.""" + + self.output_display: GroupParameter = self.add_parameter( + name="output_display", + docstring="Specifies whether the heater output displays in current or power (current mode only)", + val_mapping={"current": 1, "power": 2}, + parameter_class=GroupParameter, + ) + """Specifies whether the heater output displays in current or power (current mode only)""" + + self.heater_group = Group( + [ + self.output_heater_resistance, + self.output_max_current, + self.output_max_user_current, + self.output_display, + ], + set_cmd=f"HTRSET {output_index},{{output_heater_resistance}},{{output_max_current}},{{output_max_user_current}},{{output_display}}", + get_cmd=f"HTRSET? {output_index}", + ) + + +class LakeshoreBaseOutputWithHeaterSetupAndOutputType( + LakeshoreBaseOutputWithHeaterSetup +): + def __init__( + self, + parent: "LakeshoreBase", + output_name: str, + output_index: int, + has_pid: bool = True, + **kwargs: "Unpack[InstrumentBaseKWArgs]", + ): + """ + Base class for the outputs of Lakeshore temperature controllers that + support the heater setup command in the form + ``HTRSET ,,,,,`` + (e.g. the outputs of the Model 335). + + Args: + parent: instrument that this channel belongs to + output_name: name of this output + output_index: identifier for this output that is used in VISA commands of the + instrument + has_pid: if True, then the output supports closed loop control, + hence it will have three parameters to set it up: 'P', 'I', and 'D' + **kwargs: Forwarded to baseclass. + + """ + super().__init__(parent, output_name, output_index, has_pid=has_pid, **kwargs) + + self.output_type: GroupParameter = self.add_parameter( + name="output_type", + docstring="Output type (Output 2 only): 0=Current, 1=Voltage", + val_mapping=( + {"current": 0, "voltage": 1} if output_index == 1 else {"current": 0} + ), + parameter_class=GroupParameter, + ) + """Output type (Output 2 only): 0=Current, 1=Voltage""" + + # The output type is the first field of this HTRSET command format, + # hence redefine the heater setup group of the parent class + self.heater_group = Group( + [ + self.output_type, + self.output_heater_resistance, + self.output_max_current, + self.output_max_user_current, + self.output_display, + ], + set_cmd=f"HTRSET {output_index},{{output_type}},{{output_heater_resistance}},{{output_max_current}},{{output_max_user_current}},{{output_display}}", + get_cmd=f"HTRSET? {output_index}", + ) + + class LakeshoreBaseSensorChannel(InstrumentChannel): # A dictionary of sensor statuses that assigns a string representation of # the status to a status bit weighting (e.g. {4: 'VMIX OVL'}) From 3e7a624a44c93283f7fd54672470c66062a07fc9 Mon Sep 17 00:00:00 2001 From: Nikolaos Chatzaras <39853363+SnX59@users.noreply.github.com> Date: Fri, 2 Oct 2026 10:20:29 +0200 Subject: [PATCH 5/7] Fix setter query formatting in lakeshore_model336.yaml Removed spaces in setter queries for consistency. --- src/qcodes/instrument/sims/lakeshore_model336.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qcodes/instrument/sims/lakeshore_model336.yaml b/src/qcodes/instrument/sims/lakeshore_model336.yaml index 837249624d7e..b72f35276830 100644 --- a/src/qcodes/instrument/sims/lakeshore_model336.yaml +++ b/src/qcodes/instrument/sims/lakeshore_model336.yaml @@ -306,7 +306,7 @@ devices: q: "HTRSET? 1" r: "{}" setter: - q: "HTRSET 1, {}" + q: "HTRSET 1,{}" htrset_output_2: default: "2,2,+1.7320E+00,1" @@ -314,7 +314,7 @@ devices: q: "HTRSET? 2" r: "{}" setter: - q: "HTRSET 2, {}" + q: "HTRSET 2,{}" ramp_output_1: default: "0,0" From dc5878a07c094f0797df1a7c0687ca048cf5fcf0 Mon Sep 17 00:00:00 2001 From: Nikolaos Chatzaras <39853363+SnX59@users.noreply.github.com> Date: Fri, 2 Oct 2026 14:09:50 +0200 Subject: [PATCH 6/7] Same HTRSET fix to the Model 372 driver The earlier fix LakeshoreBaseOutput no longer adds the heater setup parameters. The 4-field class LakeshoreBaseOutputWithHeaterSetup is used by 336 outputs 1 and 2. The 5-field class with an output type is used by the 335. 336 outputs 3 and 4 get no heater setup parameters. The 372 had only been moved onto the 5-field class so that its behaviour wouldn't change. --- .../Lakeshore/Lakeshore_model_372.py | 141 +++++++++++++++++- .../instrument_drivers/Lakeshore/__init__.py | 8 + .../Lakeshore/lakeshore_base.py | 4 +- 3 files changed, 145 insertions(+), 8 deletions(-) diff --git a/src/qcodes/instrument_drivers/Lakeshore/Lakeshore_model_372.py b/src/qcodes/instrument_drivers/Lakeshore/Lakeshore_model_372.py index 0596f8797ab0..b2d29d6b40b4 100644 --- a/src/qcodes/instrument_drivers/Lakeshore/Lakeshore_model_372.py +++ b/src/qcodes/instrument_drivers/Lakeshore/Lakeshore_model_372.py @@ -3,7 +3,7 @@ import qcodes.validators as vals from qcodes.instrument_drivers.Lakeshore.lakeshore_base import ( LakeshoreBase, - LakeshoreBaseOutputWithHeaterSetupAndOutputType, + LakeshoreBaseOutput, LakeshoreBaseSensorChannel, ) from qcodes.parameters import Group, GroupParameter @@ -17,8 +17,16 @@ _n_channels = 16 -class LakeshoreModel372Output(LakeshoreBaseOutputWithHeaterSetupAndOutputType): - """An InstrumentChannel for control outputs (heaters) of Lakeshore Model 372""" +class LakeshoreModel372Output(LakeshoreBaseOutput): + """ + An InstrumentChannel for control outputs (heaters) of Lakeshore Model 372 + + This class is used for the analog (still) heater, which does not support + the heater setup (``HTRSET``) command. The sample heater and the warm-up + heater use the subclasses :class:`LakeshoreModel372SampleHeater` and + :class:`LakeshoreModel372WarmupHeater`, which add the parameters of that + command. + """ MODES: ClassVar[dict[str, int]] = { "off": 0, @@ -105,6 +113,121 @@ def __init__( self.D.vals = vals.Numbers(0, 2500) +class LakeshoreModel372SampleHeater(LakeshoreModel372Output): + """ + An InstrumentChannel for the sample heater (output 0) of Lakeshore Model 372 + + Unlike the Model 335, the heater setup (``HTRSET``) command of the + Model 372 has no output type field. The max current fields of this command + only apply to the warm-up heater, hence the sample heater only has the + heater resistance and current/power display parameters. + """ + + def __init__( + self, + parent: "LakeshoreModel372", + output_name: str, + output_index: int, + **kwargs: "Unpack[InstrumentBaseKWArgs]", + ) -> None: + super().__init__(parent, output_name, output_index, **kwargs) + + self.output_heater_resistance: GroupParameter = self.add_parameter( + name="output_heater_resistance", + docstring="Heater load in ohms (1 to 2000)", + vals=vals.Numbers(1, 2000), + unit="Ohm", + get_parser=float, + parameter_class=GroupParameter, + ) + """Heater load in ohms (1 to 2000)""" + + self.output_display: GroupParameter = self.add_parameter( + name="output_display", + docstring="Specifies whether the heater output displays in current or power", + val_mapping={"current": 1, "power": 2}, + parameter_class=GroupParameter, + ) + """Specifies whether the heater output displays in current or power""" + + # The manual requires zeros for the max current and max user current + # fields when configuring the sample heater + self.heater_group = Group( + [self.output_heater_resistance, self.output_display], + set_cmd=f"HTRSET {output_index},{{output_heater_resistance}},0,0,{{output_display}}", + get_cmd=f"HTRSET? {output_index}", + get_parser=self._parse_heater_setup, + ) + + @staticmethod + def _parse_heater_setup(response: str) -> dict[str, str]: + resistance, _, _, display = response.split(",") + return {"output_heater_resistance": resistance, "output_display": display} + + +class LakeshoreModel372WarmupHeater(LakeshoreModel372Output): + """ + An InstrumentChannel for the warm-up heater (output 1) of Lakeshore Model 372 + + Unlike the Model 335, the heater setup (``HTRSET``) command of the + Model 372 has no output type field. + """ + + def __init__( + self, + parent: "LakeshoreModel372", + output_name: str, + output_index: int, + **kwargs: "Unpack[InstrumentBaseKWArgs]", + ) -> None: + super().__init__(parent, output_name, output_index, **kwargs) + + self.output_heater_resistance: GroupParameter = self.add_parameter( + name="output_heater_resistance", + docstring="Heater Resistance Setting: 25/50ohm", + val_mapping={"25ohm": 1, "50ohm": 2}, + parameter_class=GroupParameter, + ) + """Heater Resistance Setting: 25/50ohm""" + + self.output_max_current: GroupParameter = self.add_parameter( + name="output_max_current", + docstring="Specifies the maximum heater output current: User Specified, 0.45 A, 0.63 A", + val_mapping={"user": 0, "0.45A": 1, "0.63A": 2}, + parameter_class=GroupParameter, + ) + """Specifies the maximum heater output current: User Specified, 0.45 A, 0.63 A""" + + self.output_max_user_current: GroupParameter = self.add_parameter( + name="output_max_user_current", + docstring="Specifies the maximum heater output current if max current is set to User Specified.", + vals=vals.Numbers(0, 0.63), + unit="A", + get_parser=float, + parameter_class=GroupParameter, + ) + """Specifies the maximum heater output current if max current is set to User Specified.""" + + self.output_display: GroupParameter = self.add_parameter( + name="output_display", + docstring="Specifies whether the heater output displays in current or power", + val_mapping={"current": 1, "power": 2}, + parameter_class=GroupParameter, + ) + """Specifies whether the heater output displays in current or power""" + + self.heater_group = Group( + [ + self.output_heater_resistance, + self.output_max_current, + self.output_max_user_current, + self.output_display, + ], + set_cmd=f"HTRSET {output_index},{{output_heater_resistance}},{{output_max_current}},{{output_max_user_current}},{{output_display}}", + get_cmd=f"HTRSET? {output_index}", + ) + + class LakeshoreModel372Channel(LakeshoreBaseSensorChannel): """ An InstrumentChannel representing a single sensor on a Lakeshore Model 372. @@ -351,16 +474,20 @@ def __init__( heaters = {"sample_heater": 0, "warmup_heater": 1, "analog_heater": 2} - self.sample_heater: LakeshoreModel372Output = self.add_submodule( + self.sample_heater: LakeshoreModel372SampleHeater = self.add_submodule( "sample_heater", - LakeshoreModel372Output(self, "sample_heater", heaters["sample_heater"]), + LakeshoreModel372SampleHeater( + self, "sample_heater", heaters["sample_heater"] + ), ) """ Sample heater output channel. """ - self.warmup_heater: LakeshoreModel372Output = self.add_submodule( + self.warmup_heater: LakeshoreModel372WarmupHeater = self.add_submodule( "warmup_heater", - LakeshoreModel372Output(self, "warmup_heater", heaters["warmup_heater"]), + LakeshoreModel372WarmupHeater( + self, "warmup_heater", heaters["warmup_heater"] + ), ) """ Warm-up heater output channel. diff --git a/src/qcodes/instrument_drivers/Lakeshore/__init__.py b/src/qcodes/instrument_drivers/Lakeshore/__init__.py index cb2a67d415bf..eb0eb590399a 100644 --- a/src/qcodes/instrument_drivers/Lakeshore/__init__.py +++ b/src/qcodes/instrument_drivers/Lakeshore/__init__.py @@ -6,6 +6,8 @@ from .lakeshore_base import ( LakeshoreBase, LakeshoreBaseOutput, + LakeshoreBaseOutputWithHeaterSetup, + LakeshoreBaseOutputWithHeaterSetupAndOutputType, LakeshoreBaseSensorChannel, ) from .Lakeshore_model_325 import ( @@ -25,11 +27,15 @@ LakeshoreModel372, LakeshoreModel372Channel, LakeshoreModel372Output, + LakeshoreModel372SampleHeater, + LakeshoreModel372WarmupHeater, ) __all__ = [ "LakeshoreBase", "LakeshoreBaseOutput", + "LakeshoreBaseOutputWithHeaterSetup", + "LakeshoreBaseOutputWithHeaterSetupAndOutputType", "LakeshoreBaseSensorChannel", "LakeshoreModel325", "LakeshoreModel325Curve", @@ -46,4 +52,6 @@ "LakeshoreModel372", "LakeshoreModel372Channel", "LakeshoreModel372Output", + "LakeshoreModel372SampleHeater", + "LakeshoreModel372WarmupHeater", ] diff --git a/src/qcodes/instrument_drivers/Lakeshore/lakeshore_base.py b/src/qcodes/instrument_drivers/Lakeshore/lakeshore_base.py index b07ced780f81..c95a9985eaf1 100644 --- a/src/qcodes/instrument_drivers/Lakeshore/lakeshore_base.py +++ b/src/qcodes/instrument_drivers/Lakeshore/lakeshore_base.py @@ -43,7 +43,9 @@ def __init__( (``HTRSET``) command because not all outputs support it and its format differs between models. Outputs that support it should subclass :class:`LakeshoreBaseOutputWithHeaterSetup` or - :class:`LakeshoreBaseOutputWithHeaterSetupAndOutputType` instead. + :class:`LakeshoreBaseOutputWithHeaterSetupAndOutputType` instead, + or add model specific heater setup parameters (as the sample and + warm-up heaters of the Model 372 do). Args: parent: instrument that this channel belongs to From 27f639745d4901060dc05aea25bc8fb7cb4a9489 Mon Sep 17 00:00:00 2001 From: Nikolaos Chatzaras <39853363+SnX59@users.noreply.github.com> Date: Fri, 2 Oct 2026 14:11:43 +0200 Subject: [PATCH 7/7] Add heater set output configurations to YAML --- .../instrument/sims/lakeshore_model372.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/qcodes/instrument/sims/lakeshore_model372.yaml b/src/qcodes/instrument/sims/lakeshore_model372.yaml index c3ac32028010..4f7112a597e9 100644 --- a/src/qcodes/instrument/sims/lakeshore_model372.yaml +++ b/src/qcodes/instrument/sims/lakeshore_model372.yaml @@ -898,6 +898,14 @@ devices: setter: q: "SETP 0,{}" + htrset_output_0: + default: "+120.000,0,+0.000,2" + getter: + q: "HTRSET? 0" + r: "{}" + setter: + q: "HTRSET 0,{}" + # ==================== # Heater Output 1 (warmup_heater) # ==================== @@ -933,6 +941,14 @@ devices: setter: q: "SETP 1,{}" + htrset_output_1: + default: "2,1,+0.000,1" + getter: + q: "HTRSET? 1" + r: "{}" + setter: + q: "HTRSET 1,{}" + # ==================== # Heater Output 2 (analog_heater) # ====================