Procedures, virtualization, abstract procedures

Demonstration of using an actuate/measure procedure to get data and analyze something about the data.

The procedure itself needs to be developed and debugged. This notebook shows how to do that virtually. When ready it goes to experiment by flipping a switch.

Experimental setup:

In [1]:
import numpy as np
import matplotlib.pyplot as plt
from IPython import display

from lightlab.laboratory.virtualization import VirtualInstrument, DualInstrument
import lightlab.laboratory.virtualization as virtUtil

Semi-libraries

These are python files in the same directory as this notebook that have some functions and classes. Import them to the notebook. You should be writing/developing them simultaneously with the notebook. This practice is recommended because .py files work well with git diff but .ipynb files do not. As a rule of thumb, don’t put instrument access within the semi-libraries. You can use them for commonly used and/or long procedures, functions, sweep declarations, etc.

In [2]:
from myProcedures import extractThreshold

This is a model of a diode

It has * parameters, like threshold voltage * methods for simulating: this applied voltage will give that observed current - apply (a.k.a. actuate) –> observe (a.k.a. measure)

It does not have state. - The observations are completely determined by the actuation now - This is not a requirement - The only reason for a simulation model to have state is if the device you are trying to model have hysteresis (or if you are caching)

In [3]:
# To debug this procedure, you will simulate a diode similar to the real one
class Diode():
    def __init__(self, threshV, Rinline=10):
        self.threshV = threshV
        self.Rinline = Rinline

    def ivResponse(self, atVoltage):
        return max(0, atVoltage - self.threshV) / self.Rinline

myDiode = Diode(threshV=.5)

# Stone age evaluation
fi, ax = plt.subplots(figsize=(6,4))         # line 0
plt.xlabel('voltage')
plt.ylabel('current')
vArr = np.linspace(0, 1, 20)
iArr = np.zeros(len(vArr))
for jV, V in enumerate(vArr):
    iArr[jV] = myDiode.ivResponse(V)
    plt.plot(vArr[:jV], iArr[:jV], '.-')
    display.clear_output(wait=True)
    display.display(plt.gcf())               # 10 line for loop
../../../_images/_static_tutorials_virtualization_ParameterExtractDemo_5_0.png
../../../_images/_static_tutorials_virtualization_ParameterExtractDemo_5_1.png

The parameter extraction procedure

A procedure consists of a sequence of actuation and measurement operations that interface with laboratory instruments. The actuation might be determined beforehand (i.e. sweep) or it could be changed depending on what is measured (i.e. search).

The procedure is often the most complicated part of your experimental code. The procedure is what you are developing and debugging here

Example, a parameter extraction type of procedure

In this example, we want to find the diode threshold voltage * Acquire: do a sweep in voltage, measuring current * Analyze: look for the maximum second-derivative

Notes

  • NdSweeper class (overkill for now) and the concept of passing methods as arguments
  • These methods are NOT called yet because
    • we don’t yet know if this procedure is real or virtual (it could be both)
In [4]:
extractThreshold?

The virtual instrument

This class basically holds the state that is normally found in real life. It interacts with the simulation model.

Why do you use the Virtual instrument instead of just using the simulator?

Because this is the API for the real life instruments. You should not have to make * prodecure 1: experimental using instruments, and * procedure 2: virtual using a simulation model, calling things like ivResponse()

VirtualKeithley provides the same API as Keithley (the real instrument class)

In [5]:
class VirtualKeithley(VirtualInstrument):
    def __init__(self, viResistiveRef):
        self.viResistiveRef = viResistiveRef
        self.__appliedVoltage = 0  # state

    def setVoltage(self, volts):
        self.__appliedVoltage = volts

    def measCurrent(self):
        return self.viResistiveRef.ivResponse(self.__appliedVoltage)

Running it

Make a diode model, connect it to the virtual keithley, execute the procedure. Then, get the extracted parameter from the procedure. Compare it to the hidden one. We are now evaluating a procedure.

In [6]:
hiddenThresh = 1.5
myDiode = Diode(hiddenThresh)
keithley = VirtualKeithley(viResistiveRef=myDiode)

foundThresh = extractThreshold(keithley, vMax=3)
err = foundThresh - hiddenThresh
print('Error =', abs(err) / hiddenThresh * 100, 'percent')
../../../_images/_static_tutorials_virtualization_ParameterExtractDemo_11_0.png
Error = 5.263157894736844 percent
../../../_images/_static_tutorials_virtualization_ParameterExtractDemo_11_2.png
../../../_images/_static_tutorials_virtualization_ParameterExtractDemo_11_3.png

Warning the following cells access real instruments

(This warning should be apparent in all your notebooks)

You can prevent any hardware access using the virtualization.virtualOnly variable.

In [7]:
virtUtil.virtualOnly = True

The hardware instrument

Is pulled from the instruments_dict. In this case, “Keithley 23.” You need to build this dict elsewhere using the tools from lightlab.laboratory.state. We don’t just want a VirtualInstrument, we want something that can switch between virtual and real on the fly. That is a DualInstrument.

In [8]:
if not virtUtil.virtualOnly:
    from lightlab.laboratory.state import lab
    dualKeithley = DualInstrument(real_obj=lab.instruments_dict['Keithley 23'],
                                   virt_obj=VirtualKeithley(myDiode))
    with dualKeithley.asReal():
        dualKeithley.setVoltage(0.)
        dualKeithley.setProtectionCurrent(50e-3)
else:
    dualKeithley = DualInstrument(virt_obj=VirtualKeithley(myDiode))
In [10]:
with dualKeithley.asVirtual():
    foundModel = extractThreshold(dualKeithley)
    print('The model threshold is', foundModel)
with dualKeithley.asReal():
    foundDevice = extractThreshold(dualKeithley)
    print('The device threshold is', foundDevice)
../../../_images/_static_tutorials_virtualization_ParameterExtractDemo_16_0.png
The model threshold is 1.4210526315789473
../../../_images/_static_tutorials_virtualization_ParameterExtractDemo_16_2.png
../../../_images/_static_tutorials_virtualization_ParameterExtractDemo_16_3.png
In [ ]: