Skip to content
Snippets Groups Projects
Commit b8d122d7 authored by Alexandros Asonitis's avatar Alexandros Asonitis
Browse files

all in one memristor

parent 5a0c2cab
No related branches found
No related tags found
No related merge requests found
"""
This is a python file containing all the important functions for memristor measurement
Available Functions
measurements in the HP4155a
plot results
create data frame
ini file decoder
enabing and disabling widgets for jupyter(lists)
"""
import sys
sys.path.insert(0, '..') #append parent directory
import module
import matplotlib.pyplot as plt
import tkinter as tk
from tkinter import filedialog
import tkinter.messagebox
import numpy as np
from IPython.display import display, clear_output
import pandas as pd
from datetime import datetime
import ipywidgets as widgets
import time
import os
#these are the quick sampling checks
def test_contacts():
device = module.HP4155a('GPIB0::17::INSTR')
device.reset()
device.measurement_mode('SAMP')
device.sampling_mode('LIN')
device.number_of_points(1)
device.integration_time('MED')
device.initial_interval(2e-3)
device.filter_status('OFF')
#remove total sampling time
device.auto_sampling_time('ON')
#disable vmus and vsus
device.disable_vsu(1)
device.disable_vsu(2)
device.disable_vmu(1)
device.disable_vmu(2)
smu = [1,2,3,4]
for i in range(1,4): # iterate through smus 1-4
for j in range(4,i,-1):
"""
We have the following pairs in order
1-4,1-3,1-2,2-4,2-3,3-4
"""
device.smu_mode_meas(i,'V') #one smu is measuring
device.smu_mode_meas(j,'COMM') #one smu is ground
#set voltage and compliance
device.constant_smu_sampling(i,0.01)
device.constant_smu_comp(i,'MAX')
#smus to remove
smu_didable = smu.copy()
smu_disable.remove(i)
smu_disable.remove(j)
for number in smu_disable:
device.smu_disable_sweep(number)
device.display_variable('X','@TIME')
device.display_variable('Y1',f'I{i}')
device.single_measurement()
while device.operation_completed() == False:
time.sleep(2)
device.autoscaling()
try:
V = device.return_data(f'V{i}')
I = device.return_data(f'I{i}')
R = V[0]/I[0]
print(f"R{ij}:{R} Ohm")
except:
print(f"Contact check of smu{i} and smu{j} failed!")
#double sweep from start to stop and then from start to stop
def sweep(start,stop,step,comp,integration,device):
device.measurement_mode('SWE')
#changed smu2 is source and 4 is ground
#smu2 is constant and common
device.smu_mode_meas(4,'COMM')
device.smu_function_sweep(4,'CONS')
#smu4 is VAR1 and V
device.smu_mode_meas(2,'V')
device.smu_function_sweep(2,'VAR1')
device.integration_time(integration)
#define double sweep
device.var1_mode('DOUB')
#start stop step and comp
device.start_value_sweep(start)
#time.sleep(5)
device.stop_value_sweep(stop)
#time.sleep(5)
if start < stop and step < 0 :
step = -step
elif start > stop and step > 0 :
step = -step
device.step_sweep(step)
#time.sleep(5)
device.comp('VAR1',comp)
#display variables
device.display_variable('X','V2')
device.display_variable('Y1','I2')
#execute measurement
device.single_measurement()
while device.operation_completed()==False:
time.sleep(2)
device.autoscaling()
#return values
V=device.return_data('V2')
I=device.return_data('I2')
#convert the list to np.array to return the absolute values for the logarithmic scale
V = np.array(V)
I = np.array(I)
#return all values to the function
return V, I
#sampling check
def sampling_check(voltage,device):
device.measurement_mode('SAMP')
device.smu_mode_meas(2,'V')
device.smu_mode_meas(4,'COMM')
#set voltage and compliance
device.constant_smu_sampling(2,voltage)
device.constant_smu_comp(2,'MAX')
device.sampling_mode('LIN')
device.number_of_points(5)
device.integration_time('MED')
device.initial_interval(2e-3)
device.filter_status('OFF')
#remove total sampling time
device.auto_sampling_time('ON')
device.display_variable('X','@TIME')
device.display_variable('Y1','R')
device.single_measurement()
while device.operation_completed() == False:
time.sleep(2)
device.autoscaling()
try:
TIME = device.return_data('@TIME')
R = device.return_data('R')
TIME = np.array(TIME)
R = np.array(R)
R_mean = np.average(R)
return R_mean
except:
return 0
#new (retention)
def retention(voltage,period,duration,device):
device.measurement_mode('SAMP')
device.smu_mode_meas(2,'V')
device.smu_mode_meas(4,'COMM')
#set voltage and compliance
device.constant_smu_sampling(2,voltage)
device.constant_smu_comp(2,'MAX')
device.sampling_mode('LIN')
device.initial_interval(period)
device.total_sampling_time(duration)
if int(duration/period)+1<=10001:
device.number_of_points(int(duration/period)+1)
else:
device.number_of_points('MAX')
device.integration_time('MED')
device.filter_status('OFF')
device.display_variable('X','@TIME')
device.display_variable('Y1','R')
device.single_measurement()
while device.operation_completed() == False:
time.sleep(2)
device.autoscaling()
try:
TIME = device.return_data('@TIME')
R = device.return_data('R')
TIME = np.array(TIME)
R = np.array(R)
return TIME,R
except:
return 0,0
#plot sweep results
def plot_sweep(x,y,title):
#plot results
plt.figure().clear()
fig, (ax1, ax2) = plt.subplots(2,sharex=True,figsize=(8,6)) #the plots share the same x axis
fig.suptitle(title)
ax1.set_title('Linear I')
ax1.set(xlabel='Voltage(V)',ylabel='Current(A)')
ax2.set_title('Logarithmic I')
ax2.set(xlabel='Voltage(V)',ylabel='Current(A)')
ax2.set_yscale('log')
ax1.plot(x,y)
ax2.plot(x,np.absolute(y))
plt.tight_layout()
plt.show()
def plot_retention(x,y):
fig, ax = plt.subplots()
fig.suptitle('Retention')
ax.set(xlabel='time(s)',ylabel='Resistance(Ohm)')
ax.set_yscale('log')
ax.set_xscale('linear')
plt.plot(x,y)
plt.show()
def create_data_frame(x,y):
header = ['V(V)','ABSV(V)',"I(A)",'ABSI(A)',"R(Ohm)"]
data = {header[0]:x,header[1]:np.absolute(x),header[2]:y,header[3]:np.absolute(y),header[4]:np.divide(x,y)}
df = pd.DataFrame(data)
#print(df)
return df
def create_retention_data_frame(x,y):
header = ['Time(s)','R(Ohm)']
data = {header[0]:x,header[1]:y}
df = pd.DataFrame(data)
return df
#write results to file
def write_to_file(file,title,df):
with open(file,'a') as f:
f.write(title)
f.write("\n")
f.write(df.to_string())
f.write("\n\n")
#### new functions ##############
def disable_widgets(widgets_list):
for widget in widgets_list:
widget.disabled = True
def change_state(widgets_list):
for widget in widgets_list:
widget.disabled = not widget.disabled
def enable_widgets(widgets_list):
for widget in widgets_list:
widget.disabled = False
#a check values function
def check_values(step,set_voltage,reset_voltage):
valid = True
root = tk.Tk()
root.withdraw()
root.lift() #show window above all other applications
root.attributes("-topmost", True)#window stays above all other applications
if step > abs(set_voltage) or step > abs(reset_voltage) or step==0:#invalid parameter setting
valid = False
tkinter.messagebox.showerror(message="Invalid parameter setting!")
#now if the set-reset voltages have the same polarity show a warning
elif set_voltage*reset_voltage>0:
valid = tk.messagebox.askokcancel(message="Set-Reset voltages have the same polarity. Continue?")
else:
pass
root.destroy()
return valid
def information_box(information):
#open dialog and hide the main window
root = tk.Tk()
root.withdraw()
root.lift() #show window above all other applications
root.attributes("-topmost", True)#window stays above all other applications
#display meaagebox
tkinter.messagebox.showinfo(message=information)
root.destroy()
#choose directory to save measurement results
#and check if you have access
def check_writable(folder):
filename = "test.txt"
file = os.path.join(folder,filename)
#protection against removing existing file in python
i=1
while os.path.exists(file):
filename=f"test{i}.txt"
file = os.path.join(folder,filename)
try:
with open(file,'a'):
writable = True
os.remove(file)
except:
writable = False
information_box(f"{folder} is not writable!")
return writable
def choose_folder():
root = tk.Tk()
root.withdraw()
root.lift() #show window above all other applications
root.attributes("-topmost", True)#window stays above all other applications
#choose nonemty folder
folder = tk.filedialog.askdirectory()
while folder == '':
folder = tk.filedialog.askdirectory()
#check if writable in a while loop
writable=check_writable(folder)
while writable == False:
#choose a correct folder
folder = tk.filedialog.askdirectory()
while folder == '':
folder = tk.filedialog.askdirectory()
#check writable if not repeat
writable=check_writable(folder)
root.destroy()
return folder
#create or append to file a new measurement(now locally) we dont need that anymore!!!
def create_remote_file(sample_series,field,DUT,folder):
filename=f"{sample_series.value}_{field.value}_{DUT.value}.txt"
file=os.path.join(folder,filename)#the whole file with location
date = str(datetime.today().replace(microsecond=0))
#check loop (once the return is called the function is over)
while True:
try:#you cannot write in every directory
with open(file,'a') as f:
title = f"Memristor Measurement"+"\n\n"+f"Sample series:{sample_series.value}" +"\n"+f"field:{field.value}"+"\n"+f"DUT:{DUT.value}"+"\n"+f"Date:{date}"+"\n\n"
f.write(title)
return file
except:
information_box(f"You cannot write in the directory: {folder}!")
#again
folder=choose_folder()
file=os.path.join(folder,filename)#the whole file with location
#write the header
def write_header(file,sample_series,field,DUT):
date = str(datetime.today().replace(microsecond=0))
with open(file,'a') as f:
title = f"Memristor Measurement"+"\n\n"+f"Sample series:{sample_series.value}" +"\n"+f"field:{field.value}"+"\n"+f"DUT:{DUT.value}"+"\n"+f"Date:{date}"+"\n\n"
f.write(title)
"""
New function (UPLOAD RESULTS)
IMPORTANT FOR ALL MEASUREMENTS
THE RESULTS ARE MOVED FROM SOURCE FILE TO TARGET FILE EVEN LOCALLY
"""
def upload_results(source_file,target_file,target_file_dir):
while True:
try:
with (open(source_file,'r') as source,open(target_file,'a') as target):
target.write(source.read())
os.remove(source_file)
return source_file,target_file,target_file_dir
except:
information_box(f"{target_file} is no longer accessible. Please change directory")
target_file_dir = choose_folder()
filename = os.path.basename(target_file)
target_file =os.path.join(target_file_dir,filename)
#and then try again
#setup device for regular memristor measurement
def setup_memristor():
#connect to the device
device = module.HP4155a('GPIB0::17::INSTR')
device.reset()
#disable all irrelevant units for the measurement
#smu1 and smu3 are disabled
device.smu_disable_sweep(1)
device.smu_disable_sweep(3)
#disable vmus and vsus
device.disable_vsu(1)
device.disable_vsu(2)
device.disable_vmu(1)
device.disable_vmu(2)
# R user function
device.user_function('R','OHM','V2/I2')
return device
\ No newline at end of file
import sys
sys.path.insert(0, '..') #append parent directory
import ipywidgets as widgets
import tkinter as tk
from tkinter import filedialog
import tkinter.messagebox
import os
from datetime import datetime
import matplotlib.pyplot as plt
import numpy as np
import module
import time
import pandas as pd
from IPython.display import display, clear_output
#widgets interactivity
def add_widgets_to_list(source_dictionary,target_list):
for widget in source_dictionary.values():
target_list.append(widget)
def check_pulse(dictionary):
#check if number of pulses is ok
if dictionary['pulses'].value < 0:
dictionary['pulses'].value = -dictionary['pulses'].value
elif dictionary['pulses'].value==0:
dictionary['pulses'].value = 1
else:
pass
# Restriction: pulse period ≥ pulse width + 4 ms
if dictionary['period'].value < dictionary['width'].value+4e-3:
dictionary['period'].value = dictionary['width'].value+4e-3
#sweep pulse measurement
def sweep_meas(dict):
device = module.HP4155a('GPIB0::17::INSTR')
device.reset()
device.smu_disable_sweep(1)
device.smu_disable_sweep(3)
#disable vmus and vsus
device.disable_vsu(1)
device.disable_vsu(2)
device.disable_vmu(1)
device.disable_vmu(2)
device.measurement_mode("SWE")
device.smu_function_sweep(2,"VAR1")
device.smu_mode_meas(4,"COMMON")
device.smu_function_sweep(4,"CONS")
device.smu_mode_meas(2,"VPULSE")
device.start_value_sweep(dict["start"].value)
device.stop_value_sweep(dict["stop"].value)
#define the number of steps given specific pulses
step = (dict["stop"].value-dict["start"].value)/(dict["pulses"].value-1)
device.step_sweep(step)
device.comp("VAR1",dict["comp"].value)
device.display_variable("X","V2")
device.display_variable("Y1",'I2')
device.range_mode(4,"AUTO")
device.range_mode(2,"AUTO")
device.pulse_base(dict["base"].value)
device.pulse_width(dict["width"].value)
device.pulse_period(dict["period"].value)
device.integration_time(dict["integration"].value)
t0 = time.time()
device.single_measurement()
while device.operation_completed()== False:
pass
t1 = time.time()
# get the execution time
elapsed_time = t1 - t0
device.autoscaling()
I_i=device.return_data("I2")
V_i=device.return_data("V2")
R_i = np.divide(V_i,I_i)
expected_time = dict["period"].value*dict["pulses"].value
times = (elapsed_time,expected_time)
values = (V_i,I_i,R_i)
del device
return times,values
def plot_sweep(values):
fig, ax1 = plt.subplots()
color = 'tab:red'
ax1.set_xlabel("V(V)")
ax1.set_ylabel("I(A)",color=color)
ax1.set_yscale('log')
ax1.plot(values[0],np.abs(values[1]),color=color)
ax1.tick_params(axis ='y', labelcolor = color,which = 'both')
# Adding Twin Axes
ax2 = ax1.twinx()
color = 'tab:green'
ax2.set_ylabel("R(Ohm)",color = color)
ax2.plot(values[0],np.abs(values[2]),color = color)
ax2.tick_params(axis ='y', labelcolor = color,which = 'both')
ax2.set_yscale('log')
fig.suptitle("Sweep Pulse Measurement Results")
plt.show()
def save_sweep(folder,sample_dict,values,times,sweep_dict):
filename = f"{sample_dict['series'].value}_{sample_dict['field'].value}_{sample_dict['dut'].value}.txt"
file = os.path.join(folder,filename)
with open(file,"a") as f:
date = str(datetime.today().replace(microsecond=0))
f.write(f"Sweep Pulse Measurement at {date}"+"\n")
f.write(f"period(s):{sweep_dict['period'].value}"+"\n")
f.write(f"width(s):{sweep_dict['width'].value}"+"\n")
f.write(f"base value(V):{sweep_dict['base'].value}"+"\n")
f.write(f"execution time(s):{times[0]}"+"\n")
f.write(f"expected time(s):{times[1]}"+"\n")
f.write(f"number of pulses:{sweep_dict['pulses'].value}"+"\n")
f.write(f"current compliance(A):{sweep_dict['comp'].value}"+"\n")
f.write(f"voltage:{sweep_dict['start'].value}V to {sweep_dict['stop'].value}V"+"\n")
f.write(f"integration time:{sweep_dict['integration'].value}"+"\n\n")
zipped = list(zip(values[0],values[1], values[2]))
df = pd.DataFrame(zipped, columns=['VPULSE(V)', 'IPULSE(A)', 'RPULSE(Ohm)'])
f.write("Results Sweep Pulse:\n")
f.write(df.to_string())
f.write("\n\n\n")
def constant_meas(dict):
device = module.HP4155a('GPIB0::17::INSTR')
device.reset()
device.user_function('V','V','V2')
device.user_function('I','A','I2')
#disable vmus and vsus
device.disable_vsu(1)
device.disable_vsu(2)
device.disable_vmu(1)
device.disable_vmu(2)
device.smu_disable_sweep(1)
#device.smu_disable_sweep(3)
device.measurement_mode("SWE")
device.smu_mode_meas(2,"VPULSE")
device.smu_function_sweep(2,'CONS')
device.smu_mode_meas(4,"COMM")
device.smu_function_sweep(2,"CONS")
device.smu_function_sweep(4,'CONS')
#smu 3 is used to define the number of pulses not contacted
device.smu_mode_meas(3,'V')
device.smu_function_sweep(3,"VAR1")
device.start_value_sweep(0)
device.stop_value_sweep(10)
#define the number of steps given specific pulses
step = 10/(dict["pulses"].value-1)
device.step_sweep(step)
device.comp("VAR1","MAX")
device.const_comp(2,dict["comp"].value)
device.cons_smu_value(2,dict["voltage"].value)
device.display_variable("X","@INDEX")
device.display_variable("Y1",'I')
device.range_mode(4,"AUTO")
device.range_mode(2,"AUTO")
device.range_mode(3,"AUTO")
device.pulse_base(dict["base"].value)
device.pulse_width(dict["width"].value)
device.pulse_period(dict["period"].value)
device.integration_time(dict["integration"].value)
t0 = time.time()
device.single_measurement()
while device.operation_completed()== False:
pass
t1 = time.time()
# get the execution time
elapsed_time = t1 - t0
device.autoscaling()
I_i=device.return_data("I")
V_i=device.return_data("V")
R_i = np.divide(V_i,I_i)
expected_time = dict["period"].value*dict["pulses"].value
times = (elapsed_time,expected_time)
values = (V_i,I_i,R_i)
del device
return times,values
def plot_constant(values):
index =[]
for i in range(len(values[0])):
index.append(i+1)
fig, ax1 = plt.subplots()
color = 'tab:red'
ax1.set_xlabel("Index(Pulse number)")
ax1.set_ylabel("I(A)",color=color)
ax1.set_yscale('log')
ax1.plot(index,np.abs(values[1]),color=color,label = "Voltage(V):"+str(min(values[0])))
ax1.tick_params(axis ='y', labelcolor = color,which = 'both')
# Adding Twin Axes
ax2 = ax1.twinx()
color = 'tab:green'
ax2.set_ylabel("R(Ohm)",color = color)
ax2.plot(index,np.abs(values[2]),color=color)
ax2.set_yscale('log')
ax2.tick_params(axis ='y', labelcolor = color,which = 'both')
fig.suptitle("Constant Pulse Measurement Results")
ax1.set_title("Voltage:"+str(min(values[0]))+"V")
plt.show()
def save_constant(folder,sample_dict,values,times,cons_dict):
filename = f"{sample_dict['series'].value}_{sample_dict['field'].value}_{sample_dict['dut'].value}.txt"
file = os.path.join(folder,filename)
with open(file,"a") as f:
date = str(datetime.today().replace(microsecond=0))
f.write(f"Constant Pulse Measurement at {date}"+"\n")
f.write(f"period(s):{cons_dict['period'].value}"+"\n")
f.write(f"width(s):{cons_dict['width'].value}"+"\n")
f.write(f"base value(V):{cons_dict['base'].value}"+"\n")
f.write(f"execution time(s):{times[0]}"+"\n")
f.write(f"expected time(s):{times[1]}"+"\n")
f.write(f"number of pulses:{cons_dict['pulses'].value}"+"\n")
f.write(f"current compliance(A):{cons_dict['comp'].value}"+"\n")
f.write(f"constant voltage:{cons_dict['voltage'].value}V"+"\n")
f.write(f"integration time:{cons_dict['integration'].value}"+"\n\n")
zipped = list(zip(values[0],values[1], values[2]))
df = pd.DataFrame(zipped, columns=['VPULSE(V)', 'IPULSE(A)', 'RPULSE(Ohm)'])
f.write("Results Constant Pulse:\n")
f.write(df.to_string())
f.write("\n\n\n")
import ipywidgets as widgets
#sample interface
style = {'description_width': 'initial'}
def constant_pulse():
voltage = widgets.BoundedFloatText(
value = 10,
min = -100,
max = 100,
step = 1,
description = 'Constant Voltage(V):',
style=style,
)
comp = widgets.BoundedFloatText(
value = 0.1,
min = -0.1,
max = 0.1,
step = 0.01,
description = 'Compliance(A):',
style=style,
)
pulses = widgets.IntText(
value = 100,
description = 'Number of Pulses:',
style=style,
)
period = widgets.BoundedFloatText(
value = 5e-3,
min = 5e-3,
max = 1,
step = 5e-3,
description ='Pulse Period(s):',
style=style,
)
width = widgets.BoundedFloatText(
value = 5e-4,
min = 5e-4,
max = 1e-1,
step= 5e-4,
description ='Pulse Width(s):',
style=style,
)
base = widgets.BoundedFloatText(
value = 0,
min = -100,
max = 100,
step = 1,
description = 'Base Voltage(V):',
style=style
)
integration =widgets.Dropdown(
options=['SHORt', 'MEDium', 'LONG'],
value='MEDium',
description='Integration:',
style=style
)
pulse_parameters = widgets.VBox([pulses,period,width,base])
smu_parameters = widgets.VBox([voltage,comp,integration])
constant_pulse_widgets = widgets.HBox([smu_parameters,pulse_parameters])
constant_pulse_dict = {
'voltage': voltage,
'comp':comp,
'pulses':pulses,
'period':period,
'width':width,
'base':base,
'integration':integration
}
return constant_pulse_widgets,constant_pulse_dict
def sweep_pulse():
start_voltage = widgets.BoundedFloatText(
value = 0,
min = -100,
max = 100,
step = 1,
description = 'Start Voltage(V):',
style=style,
)
stop_voltage = widgets.BoundedFloatText(
value = 15,
min = -100,
max = 100,
step = 1,
description = 'Stop Voltage(V):',
style=style,
)
comp = widgets.BoundedFloatText(
value = 0.1,
min = -0.1,
max = 0.1,
step = 0.01,
description = 'Compliance(A):',
style=style,
)
pulses = widgets.IntText(
value = 100,
description = 'Number of Pulses:',
style=style,
)
period = widgets.BoundedFloatText(
value = 5e-3,
min = 5e-3,
max = 1,
step = 5e-3,
description ='Pulse Period(s):',
style=style,
)
width = widgets.BoundedFloatText(
value = 5e-4,
min = 5e-4,
max = 1e-1,
step= 5e-4,
description ='Pulse Width(s):',
style=style,
)
base = widgets.BoundedFloatText(
value = 0,
min = -100,
max = 100,
step = 1,
description = 'Base Voltage(V):',
style=style
)
integration =widgets.Dropdown(
options=['SHORt', 'MEDium', 'LONG'],
value='MEDium',
description='Integration:',
style=style
)
pulse_parameters = widgets.VBox([pulses,period,width,base])
smu_parameters = widgets.VBox([start_voltage,stop_voltage,comp,integration])
sweep_pulse_widgets = widgets.HBox([smu_parameters,pulse_parameters])
sweep_pulse_dict = {
'start': start_voltage,
'stop':stop_voltage,
'comp':comp,
'pulses':pulses,
'period':period,
'width':width,
'base':base,
'integration':integration
}
return sweep_pulse_widgets,sweep_pulse_dict
This diff is collapsed.
%% Cell type:code id:df99f5a2-80af-4892-8633-33177239e444 tags:
``` python
%run memristor.py
```
%% Output
%% Cell type:code id:076a9132-edc2-4ae5-8a7f-c8a179473952 tags:
``` python
```
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment