diff --git a/hp4155/memristor_final/help.py b/hp4155/memristor_final/help.py new file mode 100644 index 0000000000000000000000000000000000000000..8de07ce54472f2de3d525a802dc8083591df30e9 --- /dev/null +++ b/hp4155/memristor_final/help.py @@ -0,0 +1,441 @@ +""" +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') + + 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.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) + + 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_disable = 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() + V = device.return_data(f'V{i}') + I = device.return_data(f'I{i}') + R = V[0]/I[0] + print(f"R{i}{j}:{R} Ohm") + #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 diff --git a/hp4155/memristor_final/help_pulse.py b/hp4155/memristor_final/help_pulse.py new file mode 100644 index 0000000000000000000000000000000000000000..ce69d42678070a3ececb8519c5050cef52678813 --- /dev/null +++ b/hp4155/memristor_final/help_pulse.py @@ -0,0 +1,439 @@ +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_pulse(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_pulse(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 + + diff --git a/hp4155/memristor_final/memristor.py b/hp4155/memristor_final/memristor.py new file mode 100644 index 0000000000000000000000000000000000000000..8db2534c51429f9ce65189fedcfff95cc84ab684 --- /dev/null +++ b/hp4155/memristor_final/memristor.py @@ -0,0 +1,641 @@ +### this is the new memrstor measurement (set and reset as many times as the user wants and full sweeps with a button) +from help import * +import ipywidgets as widgets +from keyboard import add_hotkey,remove_hotkey + +# pulsed libraries +from help_pulse import * + +#additional variables +first = True #first measurement +""" +This is not anymore the first time you start a measurement but the first time you write a header +""" + +file = None #complete filename with path +#first_sampling = True #indicates first sampling for set and reset buttons because we cannot add two at each button +#we dont need this variable anymore + + +#create temporary file to store the results localy +temp_file= os.path.join(os.getcwd(),'tempfile.txt') + +# the three naming fields + +sample_series= widgets.Text( + value= '', + placeholder ='Enter text here:', + description = 'sample series:', + style = {'description_width': 'initial'} + ) + +field = widgets.Text( + value= '', + placeholder ='Enter text here:', + description = 'Field:', + style = {'description_width': 'initial'}, + ) + +DUT = widgets.Text( + value= '', + placeholder ='Enter text here:', + description = 'DUT:', + style = {'description_width': 'initial'}, + ) + +#start new measurement button(new sample) +new=widgets.Button(description='next sample') + +#choose a new folder button +new_folder = widgets.Button(description='change folder') + + +horizontal = widgets.HBox([sample_series,new]) +horizontal3= widgets.HBox([DUT,new_folder]) +all_text_boxes = widgets.VBox([horizontal,field,horizontal3]) + + + +#first series of parameters +step = widgets.BoundedFloatText( + value=0.01, + min=0, + max=100, + step=0.01, + description='Step(V):', +) + +integration_time=widgets.Dropdown( + options=['SHORt', 'MEDium', 'LONG'], + value='MEDium', + description='Integration:', + #style = {'description_width': 'initial'}, +) + +sampling=widgets.Checkbox(description='sampling check') + +#align the widgets horizontaly +line0=widgets.HBox([step,integration_time,sampling]) + + + +# THE BUTTONS +#create buttons as it shown in the how_buttons_look +set=widgets.Button(description='SET') +reset=widgets.Button(description='RESET') +full=widgets.Button(description='FULL SWEEP') +number = widgets.BoundedIntText(value=1,min=1,max=sys.maxsize,step=1,description='full sweeps:',disabled=False) #number of measuremts for the full sweep +retention_button=widgets.Button(description='RETENTION') +contact_check = widgets.Button(description = 'CONTACT CHECK') + + +#parameter boxes +Vset=widgets.BoundedFloatText( + value=1, + min=-100, + max=100, + step=0.1, + description='Voltage(V):', +) + +#parameter buttons +CC_vset=widgets.BoundedFloatText( + value=1e-3, + min=-0.1, + max=0.1, + step=0.01, + description= 'Comp(A):', +) + +#parameter buttons +Vreset=widgets.BoundedFloatText( + value=-1, + min=-100, + max=100, + step=0.1, + description='Voltage(V):', +) + +#parameter buttons +CC_vreset=widgets.BoundedFloatText( + value=1e-3, + min=-0.1, + max=0.1, + step=0.01, + description='Comp(A):', +) + +Vretention=widgets.BoundedFloatText( + value=1, + min=-100, + max=100, + step=1, + description='Voltage(V):', +) + +period=widgets.BoundedFloatText( + value=1, + min=2e-3, + max=65.535, + step=1, + description='Period(s):', +) + +duration=widgets.BoundedFloatText( + value=60, + min=60e-6, + max=1e11, + step=1, + description='Duration(s):', +) + +#align a button with a checkbox or integer bounded texts horizontaly +line1 = widgets.HBox([set,Vset,CC_vset]) +line2 = widgets.HBox([reset,Vreset,CC_vreset]) +line3 = widgets.HBox([full,number]) +line4 = widgets.HBox([retention_button,Vretention,period,duration]) + +#pack them into a single vertical box +all = widgets.VBox([line0,line1,line2,line3,line4]) +output = widgets.Output() + + +#help lists for changing state of the buttons +information = [sample_series,field,DUT] +buttons = [set,reset,full,new,new_folder,retention_button,contact_check] +parameters = [Vset,CC_vset,Vreset,CC_vreset,step,integration_time,number,sampling,Vretention,period,duration] + +#choose folder directory +folder=choose_folder() + + +#display all at the end +display(all_text_boxes) +print() + +display(contact_check) +print() + +cons_widgets,cons_dict = constant_pulse() +sweep_widgets,sweep_dict = sweep_pulse() + +sweep_button = widgets.Button(description = "SWEEP PULSE") +cons_button = widgets.Button(description = "CONSTANT PULSE") + + +children = [all,widgets.VBox([sweep_widgets,sweep_button]),widgets.VBox([cons_widgets,cons_button])] +titles = ["Regular","Sweep Pulse","Constant Pulse"] +tab = widgets.Tab() +tab.children = children +tab.titles = titles + +display(tab,output) + +all_widgets = [sweep_button,cons_button] +add_widgets_to_list(cons_dict,all_widgets) +add_widgets_to_list(sweep_dict,all_widgets) + +#display the buttons +""" the above is what happens when the programm starts all the rest have to be written into button trigger functions""" + +def on_contact_check_clicked(b): + with output: + clear_output(wait = True) + change_state(all_widgets) + change_state(buttons) + change_state(parameters) + + test_contacts() + + information_box("Contact Check Completed") + + change_state(all_widgets) + change_state(buttons) + change_state(parameters) + +def on_set_button_clicked(b): + global first,folder,file,temp_file + with output: + #disable buttons + change_state(all_widgets) + change_state(buttons) + change_state(parameters) + + device = setup_memristor() + + #lock the device + device.inst.lock_excl() + + clear_output() + + #check values + valid = check_values(step.value,Vset.value,Vreset.value) + + #during first button press + if first == True and valid == True: + change_state(information)#disable all widgets that are relevant about the information of the sample + filename=f"{sample_series.value}_{field.value}_{DUT.value}.txt" + file = os.path.join(folder,filename) + #write header to temp_file + write_header(temp_file,sample_series,field,DUT) + first = False + + if valid == True: + if sampling.value == True: #do sampling set before set process(100mV) + R_mean_before = sampling_check(0.1,device) + R_mean_before = round(R_mean_before,1)#round 1 decimal point + print(f"Average Resistance(Sampling Check):{R_mean_before:e} Ohm") + first_sampling = False + + #execute measurement,plot results and save them + V12,I12 = sweep(0,Vset.value,step.value,CC_vset.value,integration_time.value,device) + plot_sweep(V12,I12,'SET') + df = create_data_frame(V12,I12) + print(df) + + + if sampling.value == True: #do sampling set after set process(10mV) + R_mean_after = sampling_check(0.01,device) + R_mean_after = round(R_mean_after,1) + print(f"Average Resistance(Sampling Check):{R_mean_after:e} Ohm") + first_sampling = False + + title = f"SET Memristor:"+"\n\n"+f"Set Voltage={Vset.value}V"+"\n"+f"current compliance={CC_vset.value}A"+"\n" + if sampling.value == True: + title = title + f"R(Ohm) Before/After"+"\n"+f"{R_mean_before} {R_mean_after}"+"\n" + write_to_file(temp_file,title,df) + + #upload results + temp_file,file,folder=upload_results(temp_file,file,folder) + + #show messagebox + information_box("Measurement finished!") + + #unlock device + device.inst.unlock() + + change_state(buttons) + change_state(parameters) + change_state(all_widgets) + +def on_reset_button_clicked(b): + global first,folder,file,temp_file + with output: + change_state(all_widgets) + change_state(buttons) + change_state(parameters) + + device = setup_memristor() + + #lock device + device.inst.lock_excl() + + clear_output() + + #check values + valid = check_values(step.value,Vset.value,Vreset.value) + + #during first button press + if first == True and valid == True: + #disable checkboxes, text fields etc. + change_state(information) + filename=f"{sample_series.value}_{field.value}_{DUT.value}.txt" + file = os.path.join(folder,filename) + #write header to temp_file + write_header(temp_file,sample_series,field,DUT) + first = False #set first to false irrelvant if it is in the if statement or not + + if valid == True: + if sampling.value == True: #do sampling set before reset process(10mV) + R_mean_before = sampling_check(0.01,device) + R_mean_before = round(R_mean_before,1)#round 1 decimal point + print(f"Average Resistance(Sampling Check):{R_mean_before:e} Ohm") + first_sampling = False + + #execute measurement,plot results and save them + V34,I34 = sweep(0,Vreset.value,step.value,CC_vreset.value,integration_time.value,device) + plot_sweep(V34,I34,'RESET') + df = create_data_frame(V34,I34) + print(df) + + if sampling.value == True: #do sampling set after reset process(100mV) + R_mean_after = sampling_check(0.1,device) + R_mean_after = round(R_mean_after,1) + print(f"Average Resistance(Sampling Check):{R_mean_after:e} Ohm") + first_sampling = False + + title =f"RESET Memristor:"+"\n\n"+f"Reset Voltage={Vreset.value}V"+"\n"+f"current compliance={CC_vreset.value}A"+"\n" + if sampling.value == True: + title = title + f"R(Ohm) Before/After"+"\n"+f"{R_mean_before} {R_mean_after}"+"\n" + write_to_file(temp_file,title,df) + + #upload results + temp_file,file,folder=upload_results(temp_file,file,folder) + + #show messagebox + information_box("Measurement finished!") + + #unlock device + device.inst.unlock() + + del device + + change_state(buttons) + change_state(parameters) + change_state(all_widgets) + +def on_full_button_clicked(b): + global first,folder,file,temp_file + with output: + change_state(buttons) + change_state(parameters) + change_state(all_widgets) + + device= setup_memristor() + + # lock device + device.inst.lock_excl() + + clear_output() + + #check values + valid = check_values(step.value,Vset.value,Vreset.value) + + #during first button press + if first == True and valid == True: + #disable checkboxes, text fields etc. + change_state(information) + filename=f"{sample_series.value}_{field.value}_{DUT.value}.txt" + file = os.path.join(folder,filename) + #write header to temp_file + write_header(temp_file,sample_series,field,DUT) + first = False #set first to false irrelvant if it is in the if statement or not + + + if valid == True: + with open(temp_file,'a') as f: + f.write(f"{number.value} full sweeps with parameters:") + f.write("\n") + f.write(f"Set Voltage = {Vset.value}V") + f.write("\n") + f.write(f"Current compliance set = {CC_vset.value}A") + f.write("\n") + f.write(f"Reset Voltage = {Vreset.value}V") + f.write("\n") + f.write(f"Current compliance reset = {CC_vreset.value}A") + f.write("\n\n") + + + plt.figure().clear() + fig, (ax1, ax2) = plt.subplots(2,sharex=True,figsize=(8,6)) #the plots share the same x axis + fig.suptitle('FULL SWEEP') + 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') + + stop = False + + def break_loop(): + nonlocal stop + stop = True + #help list with the resistances + resistances = [] + + add_hotkey("esc",break_loop) + #execute number of measurements + for i in range(number.value):#here it is easier to implement the sampling checks + if sampling.value == True: #before set(100mv) + R_mean_init = sampling_check(0.1,device) + R_mean_init = round(R_mean_init,1) + resistances.append(R_mean_init) + + V12,I12 = sweep(0,Vset.value,step.value,CC_vset.value,integration_time.value,device) #set + + #after set/before set + if sampling.value == True: #before set(10mv) + R_mean_set = sampling_check(0.01,device) + R_mean_set = round(R_mean_set,1) + resistances.append(R_mean_set) + + V34,I34 = sweep(0,Vreset.value,step.value,CC_vreset.value,integration_time.value,device) #reset + + #no reason to do check at the end because the next loop will do that(not anymore) more sampling checks + + #after reset + if sampling.value == True:#-0.1V + R_mean_reset = sampling_check(-0.1,device) + R_mean_reset = round(R_mean_reset,1) + resistances.append(R_mean_reset) + + + #butterfly curve + V=np.concatenate((V12,V34)) + I=np.concatenate((I12,I34)) + + #create data frame and save to file + df = create_data_frame(V,I) + f.write(f"{i+1} Iteration") + f.write("\n") + if sampling.value == True: + f.write(f"R(Ohm) INIT/SET/RESET"+"\n"+f"{R_mean_init} {R_mean_set} {R_mean_reset}"+"\n") + f.write(df.to_string()) + f.write("\n\n") + + + #plot results + ax1.plot(V,I) + ax2.plot(V,np.absolute(I)) + fig.tight_layout() + + #update plot + clear_output() + display(fig) + #plt.show() + print(df) + + #check for loop termination + if stop == True: + information_box("Endurance stopped after esc!") + f.write("endurance stopped!\n\n") + break + else: + information_box("Endurance completed!") + f.write("endurance completed!\n\n") + + remove_hotkey('esc') + stop = False + + #plot resistances if sampling value == True or len(resistances) !=0 + if len(resistances)!=0: + indexes = np.arange(1,len(resistances)+1) + resistances = np.array(resistances) + + plt.figure().clear() + fig, ax = plt.subplots() + + fig.suptitle('Resistance') + ax.set(xlabel='Index',ylabel='Resistance(Ohm)') + ax.set_yscale('log') + plt.scatter(indexes,resistances) + plt.show() + #print(len(resistances)) + #print(indexes) + + #upload results + temp_file,file,folder=upload_results(temp_file,file,folder) + + #unlock the device + device.inst.unlock() + + del device + + change_state(buttons) + change_state(parameters) + change_state(all_widgets) + +#move to next sample +def on_new_sample_button_clicked(b): + global first + with output: + #the if is to ensure that is not pressed many times + #just in case the user presses anything + change_state(buttons) + change_state(parameters) + change_state(all_widgets) + + first = True + #change_state(information) not anymore creating changing state but enabling the widgets + enable_widgets(information) + #sample_series.value='' + #field.value='' + DUT.value='' + + #enable again + change_state(buttons) + change_state(parameters) + change_state(all_widgets) + +#new_folder clicked +def on_new_folder_button_clicked(b): + global folder,file,first + with output: + change_state(buttons) #just to be sure + change_state(parameters) + change_state(all_widgets) + + folder = choose_folder()#choose new folder + #file = create_file(sample_series,field,DUT,folder) #and create the new file (creates multiple headers!!!) + first = True #that will write header if the directory is the same as the previous one! + change_state(buttons) + change_state(parameters) + change_state(all_widgets) + +def on_retention_button_clicked(b): + global first,folder,file,temp_file + with output: + change_state(buttons) + change_state(parameters) + change_state(all_widgets) + + device = setup_memristor() + + device.inst.lock_excl() + + clear_output() + + #during first button press + if first == True: + #disable checkboxes, text fields etc. + change_state(information) + filename=f"{sample_series.value}_{field.value}_{DUT.value}.txt" + file = os.path.join(folder,filename) + #write header to temp_file + write_header(temp_file,sample_series,field,DUT) + first = False #set first to false irrelvant if it is in the if statement or not + + #execute measurement + t,R=retention(Vretention.value,period.value,duration.value,device) + plot_retention(t,R) + df=create_retention_data_frame(t,R) + title =f"Retention Memristor:"+"\n\n"+f"Voltage={Vretention.value}V"+"\n"+f"period={period.value}s"+"\n"+f"duration={duration.value}s"+"\n" + + write_to_file(temp_file,title,df) + #upload results + temp_file,file,folder=upload_results(temp_file,file,folder) + #show messagebox + information_box("Measurement finished!") + + device.inst.unlock() + + del device + + change_state(buttons) + change_state(parameters) + change_state(all_widgets) + + +def on_sweep_button_clicked(b): + with output: + global first + clear_output(wait = True) + change_state(all_widgets) + change_state(buttons) + change_state(parameters) + check_pulse(sweep_dict) + + sample_dict= { + 'series':sample_series, + 'field':field, + 'dut':DUT + } + if first == True: + change_state(information) + first = False + + times,values = sweep_meas(sweep_dict) + plot_sweep_pulse(values) + save_sweep(folder,sample_dict,values,times,sweep_dict) + change_state(all_widgets) + change_state(buttons) + change_state(parameters) + + +def on_constant_button_clicked(b): + with output: + global first + clear_output(wait = True) + change_state(all_widgets) + change_state(buttons) + change_state(parameters) + check_pulse(sweep_dict) + + sample_dict= { + 'series':sample_series, + 'field':field, + 'dut':DUT + } + if first == True: + change_state(information) + first = False + + times,values = constant_meas(cons_dict) + plot_constant_pulse(values) + save_constant(folder,sample_dict,values,times,cons_dict) + change_state(all_widgets) + change_state(buttons) + change_state(parameters) + +#link buttons to widgets (pulsed) +sweep_button.on_click(on_sweep_button_clicked) +cons_button.on_click(on_constant_button_clicked) + +#link buttons with functions +set.on_click(on_set_button_clicked) +reset.on_click(on_reset_button_clicked) +full.on_click(on_full_button_clicked) +new.on_click(on_new_sample_button_clicked) +new_folder.on_click(on_new_folder_button_clicked) +retention_button.on_click(on_retention_button_clicked) +contact_check.on_click(on_contact_check_clicked) diff --git a/hp4155/memristor_final/memristor_buttons.ipynb b/hp4155/memristor_final/memristor_buttons.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..0564861132a090398cf808cddd3b309c0bfe584a --- /dev/null +++ b/hp4155/memristor_final/memristor_buttons.ipynb @@ -0,0 +1,114 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "df99f5a2-80af-4892-8633-33177239e444", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "8e42e33b13b94bc28d51ac3879635a83", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(HBox(children=(Text(value='', description='sample series:', placeholder='Enter text here:', sty…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "b645be04ed3040d5bfa6ab024e5dd698", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Button(description='CONTACT CHECK', style=ButtonStyle())" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "382a0395fff84d78b2e3ef2c67f2de6a", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Tab(children=(VBox(children=(HBox(children=(BoundedFloatText(value=0.01, description='Step(V):', step=0.01), D…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "1f0a16c6eaf44dce8926c29cff203ec7", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Output()" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%run memristor.py" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "076a9132-edc2-4ae5-8a7f-c8a179473952", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}