A simple Python 3 interface for controlling electronic measurement instruments and scripting measurement sweeps.
Project description
pyneMeas documentation (v0.0.2)
A package for simple electrical measurements using the National Instruments (NI) VISA standard. Developed by Jakob Seidl and coworkers at the Nanoelectronics group at the University of New South Wales, Sydney. It was originally written in Python 2.7 and later adapted for modern Python 3.
The package is divided into two sub-units: Instruments and utility functions that help carry out a electronic measurement. It allows you to connect to various electronic measurement equipment in few lines of code.
Currently implemented instruments include:
- Keithley 2400 series source measure units (SMUs)
- Keithley 2000 series digital multimeters
- Keithley 6517 'Electrometer'
- Stanford Instruments SRS830 Lock-in amplifier
- National Instruments USB6216 data acquisition card (NiDAQ)
- Yokogawa GS200 source measure unit
It is easy to write you own instrument classes by just following a pre-made template. See [XX].
In addition, this software features code to control Oxford Instruments superconducting magnet power supplies and read-out code to monitor the status of Oxford instruments Heliox cryostats.
A: Quickstart guide
A.1 Installing pyneMeas:
Case A: from the online repository PyPi:
$ pip install pyneMeas
Case B: from a local pyneMeas folder on your PC:
$ pip install -e local/Path/pyneMeas/.
the -e
flag allows to make changes to the original files in the folder,
useful if you want to tweak the code while working.
You can download the sourcecode (.zip) from github under
https://github.com/JakobSeidl/pyneMeas
Install the package in a virtual environment (venv) or in your global Python 3 installation, whatever works best for your use-case.
Check if the installation was successful:
$ pip list
returns a list of packages that should contain
pyneMeas 0.0.2
if you installed it from the internet (Case A) or
pyneMeas /Path/To/Local/SourceDirectory
if you installed pyneMeas from a local folder, e.g., after downloading the source code from gitHub (Case B). Enclose you path like this if it contains whitespace: "local Path/with whitespace/SourceDirectory"
You can then try loading the package as described in the following section.
A.2 Non-python drivers and hardware you'll need
IMPORTANT: In order to use commercial GPIB-controlled instruments such as Keithely source-measure units, you need to install the proprietary National Instruments VISA/USB-H bundle driver (https://www.ni.com/en-au/support/downloads/drivers/download.ni-488-2.html#306147). Possibly, this could be replaced by a fully open-source library such as pyvisa-py that supplies the back-end (https://pyvisa-py.readthedocs.io/en/latest/). For using the National Instruments Data Acquisition cards (NIDaQ), you need the corresponding driver (https://www.ni.com/en-au/support/downloads/drivers/download.ni-daqmx.html#288239).
For both we recommend not chossing the latest version but simpler, earlier versions.
A.3 Loading and testing pyneMeas:
In your python console type:
>>> import pyneMeas.Instruments as I
>>> import pyneMeas.utility as U
If this works, you can run a test measurement using 'virtual' instruments to check weather plotting and data storage works fine:
>>> U.runTest()
should open a plot window with simulated linear and sinusoidal noise signals measured over time. A progress bar should indicated the status of the measurement. In the console, you should see a notification that a folder has been created where the data will be stored.
Out: Created data storage directory: TempDat/
/>>>>>> Finished measurement A1 | Duration: 9.4 seconds = 0.2 min <<<<<<<
Setting up any basic measurement requires three steps:
- Setting up all required instruments and configuring them (see A.4)
- Defining the sweep-array and designating which instrument should sweep a variable and which instrument(s) should just read/measure (see A.5).
- Call the sweep function 'sweep()' from pyneMeas.utility using the Instrument parameters defined in 2, see A.5.
A.4 Initializing instruments:
We reccommend using an IDE such as PyCharm that supports code completion. E.g., type 'I.' (or any other alias you imported pyneMeas.Instruments as) and all implemented Instruments will appear in a drop-down list. Use TAB-completion when typing in the console. Let's use a Keithley 2401 source-measure unit:
>>> myKeithley = I.Keithley2401(10) # Initializes a Keithely2401 instrument at GPIB port 10
KEITHLEY INSTRUMENTS INC., MODEL nnnn, xxxxxxx, yyyyy/zzzzz /a/d # Parameters on the right depend on exact model/firmware
The 'KEITHLEY INSTRUMENTS INC.'
message is the instrument's response to the typical '*IDN?' query and indicates that the instrument has been successfully initiated.
Most instruments have internal options that can be set before the measurement. E.g., the Keithley source-measure
unit can be set to source voltage (and measure its output current) or to source a current instead. Every instrument has a set() and setOptions() method:
E.g.
>>> myKeithley.set('name','myKeithleyInstrumentName) # Every instrument can have a unique 'name'
>>> myKeithley.setOptions({'sourceMode':'voltage, # sourcing voltage
'sourceRange':20}) # Using high source range of up to 20 Volts
For a list of all instrument options and possible values, see [XX].
A.5 Using the sweep() function
In this example we define a simple current vs. voltage sweep with a single Keithley instrument.
We define a empty dictionary that holds all relevant data. After defining 'basePath'
and 'fileName'
under which the data will be stored, we define the 'setter'
and 'getters'
fields. They determine,
which instrument actively set (outputs) a value, e.g. a gate voltage, and which instruments measure the
resulting variables, e.g., input voltages, currents. Here we use the same Keithley instrument to do both.
The values we want to sweep over, in this case source-drain voltages, are defined in the 'sweepArray
field.
Here we use the targetArray()
function, but standard lists or numpy.arrays can be used as well.
In the last step, we call the sweep()
function with the required Dct
parameter and two optional parameters, delay
and plotVars
.
Note that the name Dct
of the dictionary may be changed but the keywords such as 'setter'
and 'sweepArray'
need to be exactly used as displayed here.
myScript.py
-----------
Dct = {} # Define empty dictionary as container
Dct['basePath'] = "Data/" # Relative to your current working directory
Dct['fileName'] = 'SampleA' # Sets the file name under which data will be saved and logged.
Dct['setters'] = {myKeithley: 'V_SD'} # Set the variable called 'V_SD' with our 'myKeithley' object
Dct['readers'] = {myKeithley: 'I_SD'} # Measure the variable called 'I_SD' with our 'myKeithley' object
Dct['sweepArray'] = U.targetArray([0,1,0],stepsize=0.1) # Define values from 0V -> 1V -> 0V in 0.1V steps
# targetArray is a utility function provided
df = U.sweep(Dct, # call sweep function and provide the Dct dictionary defined
delay=0.2, # seconds wait time in between points
plotVars = [('V_SD','I_SD')]) # plot 'I_SD' (y-axis) over 'V_SD (x-axis)'
This should open a live-plot that shows 'I_SD'
vs. 'V_SD'
. The data is saved under "Data/"
(folder is created if it doesn't exist, see console output). Per measurement, four files are saved:
The data is saved in .tsv format (blank text), in .mat matlab data format. In addition, a log.tsv file is created that logs
important information of the measurement run, such as instruments used etc. When plotting is enabled, the plot is also saved as .png.
Note that each measurement has unique measurement ID consisting of a letter prefix and a running number.
This is e.g., displayed in the plot title and each saved data file is preceded by it. This also prevents data
from being overwritten even when the user uses the same fileName
repeatedly. The sweep()
function
returns a pandas.DataFrame
, here assigned to df
, that holds the acquired data with column labels corresponding to the variable names provided.
This can be useful if the user wants to use the acquired data immediately during the measurement routine.
>>> df
Out:
V_SD I_SD
0 . .
1 . .
2 . .
.. ... ...
>>> df.plot(x = 'V_SD', y ='I_SD') # A simple way of plotting the data
B Example scripts/ Tutorials
B.1 Two variables: A gate-sweep measurement
gateSweepScript.py
------------------
Vgate_Keithley = I.Keithley2401(10)
VBias_Keithley = I.Keithley2401(11)
[Set options either manually or with .setOptions({'key':argument}) method]
Dct = {} # Define empty dictionary as container
Dct['basePath'] = "Data/"
Dct['setters'] = {Vgate_Keithley: 'V_G'}
Dct['readers'] = { VBias_Keithley: 'I_SD',
Vgate_Keithley: 'I_LeakGate'}
gateLow = -1; gateHigh = 1.5
Dct['sweepArray'] = U.targetArray([0, gateLow, gateHigh, 0], stepsize = 0.05)
sourceDrainBiases = [0.05, 0.1, 0.5] # volt
for bias in sourceDrainBiases:
Dct['fileName'] = f'SampleA_{bias}_volt' # data for each bias will have the bias in the filename for convenience, e.g. SampleA_0.5_volt'
VBias_Keithley.goTo(bias,0.01,0.2) # Go to the desired bias, then execute gate sweep.
U.sweep(Dct, delay=0.2, plotVars = [('V_G','I_SD'), ('V_G','I_LeakGate')])
B.2 Measure instruments over time
timeMeasScript.py
-----------------
myKeith = I.Keithley2401(11)
myTimeInst = I.TimeMeas() # virtual instrument
[Set myKeith options if desired ...]
Dct['basePath'] = "Data/"
Dct['fileName'] = 'SampleB_currOverTime'
Dct['setters'] = {myTimeInst: 'dummyPoints'}
Dct['readers'] = { myKeith: 'I_d',
myTimeInst: 'time'}
Dct['sweepArray'] = range(100) # measure over 100 datapoints
U.sweep(Dct, delay=0.2, # The delay here determines roughly how often we measure per unit time.
plotVars = [('time','I_d')],
breakCondition = ('time','>',50)) # optionally we can terminate the measurement after a certain time period if desired.
B.3 Simple I-V measurements with the NIDaq
simpleNiDaQ.py
--------------
DaqIn = USB6216In(2) # Using Analog Input AI2
DaqOut = USB6216Out(1) # Using Analog Output AO1
myTime = TimeMeas()
Dct = {} # Define empty dictionary as container
Dct['basePath'] = "Data/"
Dct['fileName'] = 'SampleC_IV'
Dct['setters'] = {DaqOut: 'V_SD'}
Dct['readers'] = { DaqIn: 'I_SD',
myTime: 'time'}
targetLow = 0; targetHigh = 1.5
Dct['sweepArray'] = U.targetArray([targetLow, targetHigh, targetLow], stepsize = 0.01)
df = U.sweep(Dct, # No plotting and 'delay' here to speed up the nidaq aquisition.
saveCounter = 100) # Only save data every 100th read to increase speed.
B.4 Example using lock-in amplifiers
lockIn_HallScript.py
--------------------
lockIn_ISD = I.SRS830(8)
lockIn_VSD = I.SRS830(10)
lockIn_VHall = I.SRS830(11)
keith_Vg = I.Keithley2401(13)
# You could set all these manually on the instrument,
# except 'scaleFactor' and 'autoSensitivity'
lockIn_ISD.setOptions({
'frequency':77,
'amplitude':0.004,
'input':'I1',
'scaleFactor':1,
'phase':180,
'autoSensitivity':False })
[.. set other options as desired...]
Dct = {} # Define empty dictionary as container
Dct['basePath'] = "Data/"
Dct['fileName'] = 'SampleC_IV'
myComments = """
Hall bar measurement at B = +2 T @ 4K
We measure the current through device with lockIn_ISD
and the voltage across the device with lockIn_VSD. The third lock in,
lockIn_VHall, measures the Hall voltage. We sweep the gate-voltage with
a Keithley2400 (keith_Vg). """
Dct['setters'] = {keith_Vg: 'V_g'}
Dct['readers'] = { lockIn_ISD: ['Isd_X','Isd_Y'], # The lock-in puts out TWO read-values
lockIn_VSD: ['Vsd_X','Vsd_Y'], # We put them together in brackets
lockIn_VH: ['VHall_X','VHall_Y'],
keith_Vg: 'I_gateLeak' # Instruments that only yield single values
} # don't need brackets.
targetLow = 0; targetHigh = 1.5
Dct['sweepArray'] = U.targetArray([targetLow, targetHigh, targetLow], stepsize = 0.01)
lockIn_ISD.goTo(2,0.2,0.2) # got to a excitation amplitude of 2 V (usually into a voltage divider)
data = U.sweep(Dct,
plotVars = [('V_g','I_sd_X),
('V_g','V_Hall_X),
('V_g','VHall_Y)]),
plotParams = [('b-','linear-log'), # Sets the plot appearance and lin-log x-y scale
('go','linear-linear') #linear-linear scale (default)
('ro--','linear-linear')],
comments = myComments
)
B.5 Abruptly changing signals - sourcing step functions with SMU's
Vgate_Keithley = I.Keithley2401(10)
VBias_Keithley = I.Keithley2401(11)
myTime = TimeMeas()
Dct = {} # Define empty dictionary as container
Dct['basePath'] = "Data/"
Dct['fileName'] = 'SampleD_GateTransient'
VgHigh = 0.5 # volt
VgLow = -0.1
# Creates a Low-HIgh-Low-High square wave gate potential:
Dct['sweepArray'] = [VgLow]*50 + [VgHigh]*150 + [VgLow]*50 + [VgHigh]*150
Dct['setters'] = {Vgate_Keithley: 'V_G'}
Dct['readers'] = { VBias_Keithley: 'I_SD',
Vgate_Keithley: 'I_LeakGate'
myTime: 'time'}
df = U.sweep(Dct,
plotVars = [('time','I_SD'),
('time','V_G')]
)
B.6 Reading current instrument setter status to dynamically define next sweeps
[... To be completeted soon]
# Step 1 Gate-sweep that will be stopped by breakCondition once current drops below 1E-6 A:
# Step 2: After breaking the measurement, you wish to drive the gate back up while measuring.
current = mySetter.get('sourceLevel')
C In-depth documentation on instruments and utility functions
C.1 Optional parameters of the sweep() function
delay
Float, wait time in seconds after a value has been set and before instruments are read out. Default=0.0
comments
String, Comments on experiment, sample etc. Is stored in the .log file together with
the saved data. Useful for data not measured by instruments. Example: "SampleA, I-V, T= 77K"
plotVars
list of (xVar,yVar)
tuples to be plotted. Example: [ ('V_SD', 'I_SD') ]
from above.
plotParams
list of ('plotString','XAxisScale-YAxisScale')
tuples. 'plotString'
contains color, line and marker info.
See https://matplotlib.org/3.3.3/api/_as_gen/matplotlib.pyplot.plot.html under Notes.
'XAxisScale-YAxisScale' can be e.g. 'linear-linear'
or 'linear-log'
or any combination.
Example: [ ('go-', 'linear-linear') ]
plotAlpha
Float, Transparency of markers: 1= no transparencey, 0 = fully transparent. Default = 0.8
plotCounter
Integer, After how many datapoints do you want to update the plot. plotCounter > 1 helps speed up plotting. Default = 1
plotSize
Tuple of two floats (xSize,ySize)
, size of plot window in cm. Default = (10,10)
saveCounter
Integer, After how many datapoints do you want to save data to disc.
Can help speed up the measurement slightly. Default = 10
breakCondition
Tuple, ('Variable','comparisonOperator',Value)
. Allows to stop a measurement when a certain condition is met.
'Variable'
is compared against Value
using 'comparisonOperator'
.
'comparisonOperator'
can be '<'
or '>'
at the moment. Example: breakCondition = ('I_SD','>',1E-6)
will end the
measurement when 'I_SD'
reaches a value above 1 microampere.
extraInstruments
List of instrument objects, used to keep track of instruments that are not directly used as setter or reader but you still want to see logged in the .log file.
saveEnable
Boolean, Defines wheather saving the data is desired. Default = True
C.2 Keithley 2400 series
These properties can be set, as well as queried with the get/set methods.
'outputEnable'
: Boolean, Turns instrument output on (True or 1) or off (False or 0).
outputEnable = True
is required for instrument operation.
Example: >>> myKeithley.set('outputEnable',False)
'sourceMode'
, String, can be 'voltage'
or 'current'
for sourcing a voltage or current respectiveley.
The instrument will also measure the flowing current (when sourcing 'voltage'
) or the resulting voltage (when sourcing a 'current'
).
When a Keithley2400 instrument is created, it will internally check the current source mode of the instrument, so 'sourceMode'
can be set in hardware or software.
'sourceRange':
Float, sets overall source range of instrument. Highest value you want to put out need to be below this limit.
When sourcing 'voltage'
, possible limits are: (20,10,1,0.1,0.01,0.001)
(in volts). When sourcing
'current'
, possible limits are: (1,0.1,0.01,0.001,1E-4,1E-5,1E-6)
(in amperes).
'name''
String, Optional unique name of the instrument to distinguish between various instruments of the same type.
'sourceLevel'
, Float, set method only. Directly sets the output (voltage or current) to the set value. We do not recommend using this directly, especially not for sensitive nanoscale samples.
Use myInstrument.goTo(target,stepSize,delay)
instead to slowly approach the desired setpoint.
'senseLevel'
Returns float, get method only. Returns the current voltage/current reading.
'senseRange'
, when sourcing 'voltage': (1.05E-4,1.05E-5,1.05E-6)
amperes.
OR when sourcing 'current': (21.0,2.1,0.21)
volts
compliance
, Float, Hard limit of highest current the instrument supplies when sourcing 'voltage'
or highest voltage when sourcing 'current'
. Must lie below the highest value the instrument can measure, see 'senseRange'
.
'scaleFactor'
Float, scales the measured values form the instrument. Defaults to 1 and does not usually have to be set.
C.3 Keithley 2000 series
'senseMode'
, String, determines whether the instrument reads current or voltage as input. Can be 'voltage'
or 'current'
.
'senseRange'
Float, sets the input range. Allowed voltage ranges are: (100E-3,1,10,100,1000) # volts.
Allowed current ranges are: (10E-3,100E-3,1,3) # amps.
'senseLevel'
, Returns float, get method only. Returns reading of input voltage/current.
'scaleFactor'
Float, scales the measured values form the instrument. Useful when current or voltage preamps are used and the output is read by the Keithley 2000.
C.4 Keithley 6517A Electrometer
'zeroCheck'
Boolean, True
enables zerocheck mode, which protects the instrument when not in use.
Needs to be disabled (False
) before a measurement.
senseMode'
, String, determines whether the instrument reads current or voltage as input. Can be 'voltage'
or 'current'
.
'senseLevel'
, Returns float, get method only. Returns reading of input voltage/current.
'senseRange'
Float, when measuring current: [20E-12,200E-12,2E-9,20E-9,200E-9,2E-6,20E-6,200E-6,2E-3,20E-3] # amperes.
When measuring voltage: [2,20,200] # volts.
'autoRange'
, Boolean. When True
, the instrument will try to adjust its 'senseLevel'
according to its input readings.
'NPLC'
, Float between 0.1 & 10. Sets the integration time during a measurement in 'power line cycles'. Default is 1.
Reduce this if your overall sweep velocity is too fast for the instrument to catch up, increase 'NPLC'
if you need slow, high precision readings of low currents.
C.5 SRS830 lock-in amplifier
For a detailed look at the lock-in specific parameters, please refer to the excellent manual of the SRS830 https://www.thinksrs.com/downloads/pdfs/manuals/SR830m.pdf
'frequency':
Float between 0.1 Hz & 100 kHz. Sets the operation frequency.
'amplitude':
Float between 0.004 V & 5 V. Sets the root-mean-square amplitude of the sinusoidal
output voltage of the amplifier.
'input'
String, can be "A","A-B","I1" or "I2"
corresponding to a simple voltage measurement "A"
,
a differential voltage measurement "A-B"
or a current measurement with two different gains "I1"/"I2""
'timeConst' :
Float or int. Sets the time constant of the lock-in amplifier. Allowed inputs are: [10E-6,30E-6,100E-6,300E-6,1E-3,3E-3,10E-3,30E-3,100E-3,300E-3,1,3,10,30,100,300] # seconds
'phase'
Float, sets the phase-parameter of the lock-in amplifier in degrees. Can range from 0 to 360 degrees.
'sweepParameter'
String, can be "frequency" or "amplitude"
. When the lock-in is used as a setter in the sweep() function,
you can choose whether you want to sweep the RMS amplitude (default) or output frequency. Sweeping the frequency can e.g. be used to
determine which drive frequency generates low noise background
'sourceLevel'
, Float, set method only. Directly sets the output amplitude (or frequency) to the set value. We do not recommend using this directly.
Use myInstrument.goTo(target,stepSize,delay)
instead to slowly approach the desired setpoint.
'senseLevel'
Tuple of floats (Xreading, Yreading)
, get method only. Returns the X- and Y-component of the voltage/current reading.
'senseRange'
Float, [1E-9,2E-9,5E-9,10E-9,20E-9,50E-9,100E-9,200E-9,500E-9,1E-6,2E-6,5E-6,10E-6,20E-6,50E-6,100E-6,200E-6,500E-6,1E-3,2E-3,5E-3,10E-3,20E-3,50E-3,100E-3,200E-3,500E-3,1] # volts
These voltage ranges directly correspond to current ranges, as written on the amplifier front panel.
'autoSensitivity'
Boolean. When True
, the instrument will try to adjust its 'senseLevel'
according to its input readings. Note
that this is not fully mature at the moment and should be properly tested prior to use in a real experiment. Defaults to False.
'name'
String, Optional unique name of the instrument to distinguish between various instruments of the same type.
C.6 YokogawaGS200
[Following soon]
Beyond the sweep() function: Using instruments in a custom for-loop
[Following soon]
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
Built Distribution
File details
Details for the file pyneMeas-0.0.2.tar.gz
.
File metadata
- Download URL: pyneMeas-0.0.2.tar.gz
- Upload date:
- Size: 42.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.2.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/41.4.0 requests-toolbelt/0.9.1 tqdm/4.36.1 CPython/3.7.4
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | e19f257f95bae73758a9869035cb780478790344829beaf70cf1a4b39f6935b8 |
|
MD5 | 3126231e4352a7293094e4313d371411 |
|
BLAKE2b-256 | b8d686c11efeece7505cc6af8f0488e50b6d7a47a1d2a3ade57f8cf72c7167db |
File details
Details for the file pyneMeas-0.0.2-py3-none-any.whl
.
File metadata
- Download URL: pyneMeas-0.0.2-py3-none-any.whl
- Upload date:
- Size: 41.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.2.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/41.4.0 requests-toolbelt/0.9.1 tqdm/4.36.1 CPython/3.7.4
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | f08fd17e8763052c2caf89e90489f4ad2650922eb056220fcad940bf30c6b69f |
|
MD5 | a75f49cf124e1d48bd8c1e406bee928c |
|
BLAKE2b-256 | edf30686d0eaf114ef83ea1e06bd6571e569d8e89b03c733d8cfaa567e00de02 |