Source code for semi_cr.core.lab.instrument.routable_keithley

from qcodes.instrument_drivers.Keithley import Keithley2636B
from qcodes.instrument_drivers.Keithley._Keithley_2600 import Keithley2600Channel
from semi_cr.core.lab.station.routing_v1.models import ConnectionRoutable, Routable
from collections.abc import Sequence
from typing import Any, Literal
import networkx as nx
from semi_cr.core.lab.station.graphing.models import Node, Edge
from semi_cr.core.lab.station.graphing.operations import build_nx_graph
from semi_cr.core.lab.station.graphing.ids import (
    qcodes_node_id, 
    terminal_node_id,
)
from qcodes.instrument import InstrumentModule

SMUTerminalKind = Literal[
    "force_hi",
    "force_lo",
    "sense_hi",
    "sense_lo",
]

[docs] class RoutableSMUChannel(Keithley2600Channel, Routable): @property def capabilities(self) -> frozenset[str]: return frozenset({ "voltage_source", "voltage_meter", "current_source", "current_meter", }) @property def graph(self) -> nx.MultiDiGraph: channel_id = qcodes_node_id(self) attrs = { "capabilities": { "voltage_source", "voltage_meter", "current_source", "current_meter", }, "quantities": { "voltage", "current", }, "modality": "dc", } return build_nx_graph( nodes=[ Node( id=channel_id, kind="instrument_channel", name=self.short_name, obj=self, attrs=attrs, ), ], edges=[], )
[docs] class SMUTerminal(InstrumentModule, Routable): def __init__( self, parent: "RoutableSMUChannel", name: str, terminal_type: SMUTerminalKind, ): super().__init__( parent=parent, name=name, ) self._terminal_type = terminal_type self.add_parameter( "voltage", unit="V", label=f"SMU voltage", get_cmd=self.get_voltage, set_cmd=self.set_voltage, get_parser=float, set_parser=float, ) self.add_parameter( "current", unit="V", label=f"SMU current", get_cmd=self.get_current, set_cmd=False, get_parser=float, set_parser=float, )
[docs] def set_voltage(self, voltage: float) -> None: channel = self.parent channel.volt(voltage)
[docs] def get_voltage(self) -> float: channel = self.parent return float(channel.volt())
[docs] def get_current(self) -> float: channel = self.parent return float(channel.curr())
@property def graph(self) -> nx.MultiDiGraph: channel_id = qcodes_node_id(self.parent) terminal_id = terminal_node_id( self.root_instrument.config_name, f"{self.parent.short_name}_{self.short_name}", ) polarity = ( "hi" if self._terminal_type.endswith("_hi") else "lo" ) if self._terminal_type.startswith("force"): attrs = { "terminal_type": "force", "polarity": polarity, "capabilities": { "voltage_source", "current_source", "voltage_meter", "current_meter", }, "quantities": { "voltage", "current", }, "modality": "dc", } elif self._terminal_type.startswith("sense"): attrs = { "terminal_type": "sense", "polarity": polarity, "capabilities": { "voltage_meter", # "current_meter", }, "quantities": { "voltage", # "current", }, "modality": "dc", } else: raise ValueError( f"Unknown SMU terminal type: " f"{self._terminal_type!r}" ) return build_nx_graph( nodes=[ Node( id=terminal_id, kind="terminal", name=self.short_name, obj=self, attrs=attrs, ), ], edges=[ Edge( source=channel_id, target=terminal_id, kind="represents", ), ], )
[docs] class QcodesSMU( Keithley2636B, ConnectionRoutable, ): def __init__( self, name: str, connections: Sequence[dict[str, Any]] | None = None, **kwargs: Any, ): super().__init__( name=name, **kwargs, ) self._init_connections(connections) for connection in self.connections: channel_name = connection["channel"] terminal_type = connection["terminal"] channel = getattr( self, channel_name, ) channel.add_submodule( terminal_type, SMUTerminal( parent=channel, name=terminal_type, terminal_type=terminal_type, ), )