Skip to main content

Measurement & Visualisation python framework.

Project description

Introduction

MeaVis is a python framework intended to define how Measurements have to be run and a programming interface to Visualise resulting datasets.

See more details at ReadTheDocs.io.

Basic example

General configuration of intruments

Let's assume two basic drivers as follow:

import vxi11


class AgilentB596X(vxi11.Instrument):
    def __init__(self, host):
        super().__init__(host=host)

        self.channel = None
        self.mode = None

    def conf():
        if self.channel and self.mode:
            self.write(":SOUR{}:FUNC:MODE {}".format(self.channel, self.mode))

    def output(self, value):
        self.conf()
        if self.channel:
            self.write(
                ":OUTP{} {}".format(self.channel, "ON" if value else "OFF")
            )

    def set_channel(self, channel):
        self.channel = channel
        self.write(":SOUR{}:FUNC:SHAP DC".format(self.channel))
        self.conf()

    def set_mode(self, mode):
        self.mode = mode
        self.conf()

    def set_value(self, value):
        self.write(":SOUR{}:{} {}".format(self.channel, self.mode, value))


class KeySight344XX(vxi11.Instrument):
    def __init__(self, host):
        super().__init__(host=host)

        self.write("*CLS")
        self.write("*RST")

        self.write("TRIG:SOUR IMM")

        self.write("CALC:FUNC AVER")
        self.write("CALC:STAT ON")

        self.ACorDC = None
        self.mode = None

    def calc_average(self):
        self.write("*CALC:AVER:AVER")
        return float(self.read())

    def conf(self):
        if self.ACorDC and self.mode:
            self.write("CONF:{}:{}".format(self.mode, self.ACorDC))

    def conf_ACorDC(self, ACorDC):
        self.ACorDC = ACorDC
        self.conf()

    def conf_mode(self, mode):
        self.mode = mode
        self.conf()

    def count(self, value):
        self.write("SAMP:COUN {}".format(value))

    def initiate(self):
        self.write("INIT")

    def opc(self):
        self.write("*OPC")

    def set_aperture(self, value):
        self.write("{}:APER {}".format(self.mode, value))

As you see, this driver is very close to a one-to-one correpondance between the SPCI commands and the methods. Of course, methods can be more complexe, however a simplest driver as possible allow more flexiblity.

Then classes specific to MeaVis have to be written to decribe how to use this driver:

  • Drivers: Front panel instrument.
  • MeaVis classes: How an experimentalist use the front panel.
import drivers

import meavis.tags


@meavis.tags.initialiser("power_source.current_source", mode="CURR")
@meavis.tags.initialiser("power_source.voltage_source", mode="VOLT")
class InitialiserB596X:
    def __init__(self, mode):
        self.mode = mode

    def initialise(self, handler, channel):
        handler_channel = drivers.AgilentB596X(**handler)

        handler_channel.set_channel(channel)
        handler_channel.set_mode(self.mode)
        handler_channel.output(True)

        return handler_channel


@meavis.tags.initialiser("multimeter.ac_current_meter")
@meavis.tags.kwargs(mode="CURR", ACorDC="AC")
@meavis.tags.initialiser("multimeter.ac_volt_meter")
@meavis.tags.kwargs(mode="VOLT", ACorDC="AC")
@meavis.tags.initialiser("multimeter.dc_current_meter")
@meavis.tags.kwargs(mode="CURR", ACorDC="DC")
@meavis.tags.initialiser("multimeter.dc_volt_meter")
@meavis.tags.kwargs(mode="VOLT", ACorDC="DC")
class Initialiser344XX:
    def __init__(self, mode, ACorDC):
        self.ACorDC = ACorDC
        self.mode = mode

    def initialise(self, handler, channel):
        handler_channel = drivers.KeySight344XX(**handler)

        handler_channel.conf_mode(self.mode)
        handler_channel.conf_ACorDC(True)

        return handler_channel


@meavis.tags.parameter("power_source.current_source.current")
@meavis.tags.attributes(unit="A", delay=0.1)
@meavis.tags.parameter("power_source.voltage_source.voltage")
@meavis.tags.attributes(unit="V", delay=0.1)
class SourceValue:
    def __init__(self, data):
        self.data = data

    def apply(self, handler, value):
        handler.set_value(value)


@meavis.tags.parameter("multimeter.~.aperture")
@meavis.tags.attributes(unit="s")
class DMMAperture:
    def __init__(self, data):
        self.data = data

    def apply(self, handler, value):
        handler.set_aperture(value)


@meavis.tags.parameter("multimeter.~.average_count")
class DMMCount:
    def __init__(self, data):
        self.data = data

    def apply(self, handler, value):
        handler.count(value)


@meavis.tags.measurement("multimeter.ac_current_meter|dc_current_meter.current")
@meavis.tags.measurement("multimeter.ac_volt_meter|dc_volt_meter.voltage")
class DMMAverage:
    def trigger(self, handler):
        handler.initiate()

    def wait(self, handler):
        handler.opc()
        handler.calc_average()

The elements mapped after kwargs will be used to initialise the corresponding instrument when required. For exemple, if a source is used as a voltage source, the statement handler = meavis_user.InitialiserB596X(mode="VOLT").intialise(/* */) will be executed.

This file have to be loaded as follow:

meavis.instruments.inject(meavis_user._meavis_instruments)

Note that it cannot be loaded multiple time, otherwise name collisions will happen.

Up to now the configuration is independant of what we want to measure: it only describes how to use instruments, but not how they are connect or what we want to do.

Experiment-specific configuration of intruments

First we describe how instruments are wired and for which purpose with a YAML file:

junction_bias:
  instrument: power_source
  usage: voltage_source
  kwargs:
    addr: 192.168.0.0
  attributes:
    channel: 1
junction_current:
  instrument: multimeter
  usage: dc_current_meter
  kwargs:
    host: 192.168.0.1
  attributes:
    channel: 1
junction_voltage:
  instrument: multimeter
  usage: dc_volt_meter
  kwargs:
    host: 192.168.0.1
  attributes:
    channel: 2

The elements mapped after kwargs will be used to construct the corresponding instrument when required. For exemple, the multimeter to measure the junction voltage is constructed with the statement: handler = meavis_user.ConstructorEthernet(host="192.168.0.1").create() when required. Morevoer the attribute channel: 2 is used for the initialisation handler = meavis_user.InitialiserB596X(/* */).intialise(/* */, channel=2).

This file have to be loaded as follow:

with open("instances.yaml") as file:
    meavis.instruments.register(yaml.safe_load(file))

Note that it cannot be loaded multiple time, otherwise name collisions will happen. After this step, parameters and measurements can be accessed as follow:

meavis.parameters.junction_current.aperture([10e-3])
meavis.parameters.junction_current.average_count([100])

meavis.parameters.junction_voltage.aperture([100e-3])
meavis.parameters.junction_voltage.average_count([10])

Avaibled parameters and measurements are displayed in the log output:

INFO --  Register power_source constructor [90e97748d6ea9cbb434602eb177a91c685701667] {host: 192.168.0.0}.
INFO --  Register voltage_source initialiser {mode: VOLT} for junction_bias.
INFO --  Register voltage as parameter named junction_bias.voltage.
INFO --  Register multimeter constructor [1ca226d3ca09e4167fbbfa3bd218a8323d76e12f] {host: 192.168.0.1}.
INFO --  Register dc_current_meter initialiser {mode: CURR, ACorDC: DC} for junction_current.
INFO --  Register aperture as parameter named junction_current.aperture.
INFO --  Register average_count as parameter named junction_current.average_count.
INFO --  Register current as measurement named junction_current.current.
INFO --  Register dc_volt_meter initialiser {mode: VOLT, ACorDC: DC} for junction_voltage.
INFO --  Register aperture as parameter named junction_voltage.aperture.
INFO --  Register average_count as parameter named junction_voltage.average_count.
INFO --  Register voltage as measurement named junction_voltage.voltage.
INFO --  Add completer group for junction_bias : {junction_bias.voltage}.
INFO --  Add completer group for junction_current : {junction_current.average_count, junction_current.aperture}.
INFO --  Add completer group for junction_voltage : {junction_voltage.aperture, junction_voltage.average_count}.

And finally the measurement can be described and processed as follow:

measurement_loop = meavis.loop.LoopEngine(
    yaml.safe_load(
        """
parameters:
    - junction_bias.voltage
measurements:
    - junction_current.current
    - junction_voltage.voltage
name: iv_dc_4probes
"""
)).create(
    meavis.parameters.junction_bias.voltage(numpy.linspace(-1e-3, 1e-3, 401)),
    meavis.measurements.junction_current.current(),
    meavis.measurements.junction_voltage.voltage(),
)
measurement_loop.trigger(None)
measurement_loop.wait(None)

In the log output, instruments are created and intialised when required:

INFO --  Complete parameters with [junction_current.average_count, junction_current.aperture]
INFO --  Complete parameters with [junction_voltage.aperture, junction_voltage.average_count]
INFO --  Create handler of multimeter.constructor [1ca226d3ca09e4167fbbfa3bd218a8323d76e12f] with {host: 192.168.0.1}.
INFO --  Initialise channel 2 on handler of multimeter.dc_volt_meter.initialiser with {mode: VOLT, ACorDC: DC}.
INFO --  Set junction_voltage.aperture to 0.1 s.
INFO --  Set junction_voltage.average_count to 10.
INFO --  Initialise channel 1 on handler of multimeter.dc_current_meter.initialiser with {mode: CURR, ACorDC: DC}.
INFO --  Set junction_current.aperture to 0.01 s.
INFO --  Set junction_current.average_count to 100.
INFO --  Create handler of power_source.constructor [90e97748d6ea9cbb434602eb177a91c685701667] with {host: 192.168.0.0}.
INFO --  Initialise channel 1 on handler of power_source.voltage_source.initialiser with {mode: VOLT}.
INFO --  Set junction_bias.voltage to -0.001 V.
INFO --  Trigger junction_current.current, waiting for data.
INFO --  Trigger junction_voltage.voltage, waiting for data.
INFO --  Set junction_bias.voltage to -0.000998 V.
INFO --  Trigger junction_current.current, waiting for data.
INFO --  Trigger junction_voltage.voltage, waiting for data.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

meavis-0.2.2.dev2.tar.gz (37.2 kB view hashes)

Uploaded Source

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page