Implement an Interface

Important

If you have implementations of components, please consider sharing them to help expand and improve COHESIVM. You may also simply propose the implementation of a specific device or interface.

Read the Contributing Guidelines for more information.

This tutorial will guide you through the process of implementing a new contact interface following the Interface abstract base class. To simulate a realistic use case, the tutorials are based on the measurement of the sheet resistance and resistivity of materials using a four-point probe.

Interface Class

Since we need to tell the Experiment, that the Interface and the Measurement are going to be compatible, we first have to implement an InterfaceType subclass. After that, we define the mandatory private class attributes and abstract methods of the Interface itself:

from cohesivm.interfaces import InterfaceType, Interface
from cohesivm.database import Dimensions
import fpp_connect  # our mimetic API


class FPPInterfaceType(InterfaceType):
    """Consists of two pairs of terminals which can be connected to two different device channels, e.g., one DC
    current source and one voltmeter."""


class FPP2X2(Interface):
    """This interface is an array of 2x2 measurement points, each of which consists of two contact pairs to act
    as a four-point probe.

    :param com_port: The COM port where the FPP interface is connected.
    """

    _interface_type = FPPInterfaceType
    _contact_ids = ['BL', 'BR', 'TL', 'TR']
    _contact_positions = {
        'BL': (10., 10.),
        'BR': (30., 10.),
        'TL': (10., 30.),
        'TR': (30., 30.)
    }
    _interface_dimensions = Dimensions.Rectangle(40., 40.)

    def __init__(self, com_port: str) -> None:
        super().__init__(Dimensions.Generic([-3., -1., 1., 3.], [0., 0., 0., 0.]))
        self.interface_hw = fpp_connect.Interface(com_port)

    def _select_contact(self, contact_id: str) -> None:
        self.interface_hw.select(contact_id)

As stated in the docstring, the mimetic FPP2X2 interface consists of a total of four measurement points which are labelled in the _contact_ids class attribute. The positions of these points and the overall dimensions of the interface are defined afterwards. In the constructor, the pixel_dimensions are specified, which we defined using the Generic shape to implicate the coordinates of the individual contacts on each four-point probe. As a convention, we consider the first and the last coordinate to correspond to the current source and the middle ones to correspond to the voltmeter. Further, the interface hardware is initialized where we use as stand-in a simple API that is implemented in the mimetic fpp_connect.py module.

Finally, the most important abstract _select_contact() must be implemented to perform the actual switching between contacts on the interface hardware. We just call the respective method from our mimetic API.

Example Usage

In order to test the implemented interface, we build part of the fpp_connect.py mimetic module:

_resistance = 100.


def get_resistance() -> float:
    global _resistance
    return _resistance


def set_resistance(value: float) -> None:
    global _resistance
    _resistance = value


class Interface:

    def __init__(self, com_port: str) -> None:
        self.com_port = com_port

    @staticmethod
    def select(new_contact: str) -> None:
        for contact_id, new_resistance in zip(['BL', 'BR', 'TL', 'TR'], [100., 200., 50., 1000.]):
            if contact_id == new_contact:
                set_resistance(new_resistance)

Here, we change the resistance depending on the contact that we select and provide a public function to read the value of the _resistance attribute.

Now we can test switching a contact:

>>> interface = FPP2X2('5')
... fpp_connect.get_resistance()
100.
>>> interface.select_contact('BR')
... fpp_connect.get_resistance()
200.