diff --git a/hp4155/memristor (Version 4.1)/help.py b/hp4155/memristor (Version 4.1)/help.py new file mode 100644 index 0000000000000000000000000000000000000000..18e87e7012d41c453ced6e91a69d4791ce27ab5f --- /dev/null +++ b/hp4155/memristor (Version 4.1)/help.py @@ -0,0 +1,559 @@ +""" +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 hp4155a +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 + + +#contact check between two SMUs (i,j) +def contact_check(i,j,device): + smu = [1,2,3,4] + device.measurement_mode('SAMP') + parameters ={ + 'mode' : 'LIN', + 'hold': 0, + 'interval':2e-3, + 'points': 1, + 'filter': 'OFF', + 'value':0.01, #voltage value + 'comp':0.1 #compliance value + } + + device.setup_sampling(parameters) + + device.auto_sampling_time('ON') + device.integration_time('MED') + + smu_v = device.smu_dict() + smu_v.update( + vname = f'V{i}', + iname = f'I{i}', + mode = 'V', + func = 'CONS' + ) + device.setup_smu(i,smu_v) + + smu_ground = device.smu_dict() + smu_ground.update( + vname =f'V{j}', + iname = f'I{j}', + mode = 'COMM', + func='CONS' + ) + device.setup_smu(j,smu_ground) + + #one smu is measuring + #one smu is ground + + #set voltage and compliance + device.setup_smu_sampling(i,parameters) + + #smus to remove + smu_disable = smu.copy() + smu_disable.remove(i) + smu_disable.remove(j) + + for number in smu_disable: + device.smu_disable(number) + + device.user_function(f'R{i}{j}','OHM',f'V{i}/I{i}') + device.display_variable('X','@TIME') + device.display_variable('Y1',f'R{i}{j}') + device.single_measurement() + while device.operation_completed() == False: + time.sleep(2) + + R = device.return_values(f'R{i}{j}')[0] #only the first value + print(f"R{i}{j}:{'{:.2e}'.format(R)} Ohm") + device.del_user_functions() + device.autoscaling() + return R + + +#these are all the sampling checks +def regular_contact_check(device): + resistances = {} + 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 + """ + R=contact_check(i,j,device) + resistances[f"{i}-{j}"] = R + + #convert dictionary to df + df = pd.DataFrame(resistances.items(), columns=['SMU pair', 'Resistance (Ohm)']) + return df + +def EBL(device): + # EBL are SMUs 1-4 and 2-3 + resistances = {} + for i,j in zip(range(1,3),range(4,2,-1)): #loop simultaneously 1-4,2-3 pairs + R = contact_check(i,j,device) + resistances[f"{i}-{j}"] = R + + #convert dictionary to df + df = pd.DataFrame(resistances.items(), columns=['SMU pair', 'Resistance (Ohm)']) + return df + +def OL(device): + # OL smu 3-4,1-2 + resistances= {} + for i,j in zip(range(3,0,-2),range(4,1,-2)): #loop simultaneously 3-4 , 1-2 pairs + R = contact_check(i,j,device) + resistances[f"{i}-{j}"] = R + + #convert dictionary to df + df = pd.DataFrame(resistances.items(), columns=['SMU pair', 'Resistance (Ohm)']) + return df + +#double sweep from start to stop and then from start to stop +def sweep(start,stop,step,comp,integration,device): #step cannot be negative + if start < stop and step < 0 : + step = -step + elif start > stop and step > 0 : + step = -step + + smu_v = device.smu_dict() + smu_ground = device.smu_dict() + parameters = device.var1_dict() + + smu_v.update( + iname = 'I2', + vname = 'V2', + mode = 'V', + func = 'VAR1' + ) + smu_ground.update( + iname ='I4', + vname = 'V4', + mode = 'COMM', + func = 'CONS' + ) + parameters.update( + mode ='DOUB', + start = start, + stop = stop, + step = step, + comp = comp, + pcomp = 0 + ) + + #disable smus 1 and 3 + device.measurement_mode('SWE') + device.smu_disable(1) + device.smu_disable(3) + + device.setup_smu(2,smu_v) + device.setup_smu(4,smu_ground) + device.setup_var1(parameters) + device.integration_time(integration) + + #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_values('V2') + I=device.return_values('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): + + parameters ={ + 'mode' : 'LIN', + 'hold': 0, + 'interval':2e-3, + 'points': 5, + 'filter': 'OFF', + 'value':voltage, #voltage value + 'comp':0.1 #compliance value + } + smu_v = device.smu_dict() + smu_ground = device.smu_dict() + + + smu_v.update( + iname = 'I2', + vname = 'V2', + mode = 'V', + func = 'CONS' + ) + smu_ground.update( + iname ='I4', + vname = 'V4', + mode = 'COMM', + func = 'CONS' + ) + + device.measurement_mode('SAMP') + device.smu_disable(1) + device.smu_disable(3) + device.setup_smu(2,smu_v) + device.setup_smu(4,smu_ground) + + device.setup_smu_sampling(2,parameters) + device.setup_sampling(parameters) + + device.integration_time('LONG') + + #remove total sampling time + device.auto_sampling_time('ON') + + device.user_function('R','OHM','V2/I2') + + device.display_variable('X','@INDEX') + device.display_variable('Y1','R') + device.single_measurement() + while device.operation_completed() == False: + time.sleep(2) + + index = np.array(device.return_values('@INDEX')) + R = np.array(device.return_values('R')) + R_mean = np.average(R) + device.del_user_functions() + device.autoscaling() + + # Plot the results + fig,ax = plt.subplots() + + ax.set_title(f"Average Resistance(Sampling Check):{'{:.2e}'.format(R_mean)} Ohm") + ax.set_yscale('log') + ax.set_ylabel('Resistance (Ohm)') + ax.set_xlabel('Sampling Index') + ax.set_xticks(index) + ax.scatter(index,np.absolute(R),label = f"Voltage={voltage}V") + ax.legend() + display(fig) + + return R_mean + +#new (retention) +def retention(voltage,period,duration,device): + parameters ={ + 'mode' : 'LIN', + 'hold': 0, + 'interval':2e-3, + 'points': 0, + 'filter': 'OFF', + 'value':voltage, #voltage value + 'comp':0.1 #compliance value + } + smu_v = device.smu_dict() + smu_ground = device.smu_dict() + + smu_v.update( + iname = 'I2', + vname = 'V2', + mode = 'V', + func = 'CONS' + ) + smu_ground.update( + iname ='I4', + vname = 'V4', + mode = 'COMM', + func = 'CONS' + ) + + device.measurement_mode('SAMP') + device.smu_disable(1) + device.smu_disable(3) + device.setup_smu(2,smu_v) + device.setup_smu(4,smu_ground) + + device.setup_smu_sampling(2,parameters) + + device.integration_time('LONG') + device.total_sampling_time(duration) + + if int(duration/period)+1<=10001: + parameters.update(points=int(duration/period)+1) + else: + parameters.update(points = 'MAX') + device.setup_sampling(parameters) + + device.integration_time('MED') + device.user_function('R','OHM','V2/I2') + device.user_function('ABSR','OHM', 'ABS(R)') + + + device.display_variable('X','@TIME') + device.display_variable('Y1','ABSR') + device.axis_scale('Y1','LOG') + device.display_variable_min_max('Y1','MIN',10) + device.display_variable_min_max('Y1','MAX',10**8) + + device.single_measurement() + while device.operation_completed() == False: + time.sleep(2) + + + TIME = device.return_values('@TIME') + R = device.return_values('R') + TIME = np.array(TIME) + R = np.array(R) + device.del_user_functions() + device.autoscaling() + return TIME,R + + +#plot sweep results +def plot_sweep(x,y,title): + #plot results + 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)) + fig.tight_layout() + display(fig) + +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') + ax.plot(x,y) + display(fig) + + +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) + 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:list,df): + #append escape character after each element + index = 1 + while index <= len(title): + title.insert(index,"\n") + index = index+2 + + #write to file + with open(file,'a') as f: + f.writelines(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 + + +def upload_results(source_file,target_file,target_file_dir): + """ + New function (UPLOAD RESULTS) + IMPORTANT FOR ALL MEASUREMENTS + THE RESULTS ARE MOVED FROM SOURCE FILE TO TARGET FILE EVEN LOCALLY + """ + 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 + + +#ini file functions +def save_as_ini(default_filename): + root = tk.Tk() + root.withdraw() + root.lift() #show window above all other applications + + root.attributes("-topmost", True)#window stays above all other applications + + file = filedialog.asksaveasfilename(defaultextension=".ini", filetypes=[("Ini files","*.ini")],title = "save as ini",initialfile =default_filename) + + #check if the file path is correct(.txt) + while file.endswith(".ini") == False: + #open again filedialog with error message box + answer=tk.messagebox.askyesno(message='Do you want to cancel the ini file Save?') + if answer == True: + raise Exception("Ini File Operation aborted!") + else: + file = filedialog.asksaveasfilename(defaultextension=".ini", filetypes=[("Ini files","*.ini")],title = "save as ini",initialfile =default_filename) + root.destroy() + return file + +def load_ini(): + root = tk.Tk() + root.withdraw() + root.lift() #show window above all other applications + + root.attributes("-topmost", True)#window stays above all other applications + + + file = filedialog.askopenfilename(filetypes=[("Ini files","*.ini")],title ='Select ini file') + while file.endswith(".ini") == False: + #open again filedialog with error message box + answer=tk.messagebox.askyesno(message='Do you want to cancel the ini file load?') + if answer == True: + raise Exception("Ini File Operation aborted!") + else: + file = filedialog.askopenfilename(filetypes=[("Ini files","*.ini")],title = "Select ini file") + root.destroy() + return file + + + + + + + \ No newline at end of file diff --git a/hp4155/memristor (Version 4.1)/help_pulse.py b/hp4155/memristor (Version 4.1)/help_pulse.py new file mode 100644 index 0000000000000000000000000000000000000000..a5195597a879a8520133f809c25e500d4346c8e3 --- /dev/null +++ b/hp4155/memristor (Version 4.1)/help_pulse.py @@ -0,0 +1,463 @@ +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(dictionary,device): + smu_v = device.smu_dict() + smu_ground = device.smu_dict() + parameters = device.var1_dict() + + smu_v.update( + iname = 'I2', + vname = 'V2', + mode = 'VPULSE', + func = 'VAR1' + ) + smu_ground.update( + iname ='I4', + vname = 'V4', + mode = 'COMM', + func = 'CONS' + ) + parameters.update( + mode ='SING', + start = dictionary['start'].value, + stop = dictionary['stop'].value, + step = (dictionary["stop"].value-dictionary["start"].value)/(dictionary["pulses"].value-1), #define the number of steps given specific pulses + comp = dictionary['comp'].value, + pcomp = 0, + base = dictionary["base"].value, + width = dictionary["width"].value, + period= dictionary["period"].value + ) + device.smu_disable(1) + device.smu_disable(3) + + + device.measurement_mode("SWE") + device.setup_smu(2,smu_v) + device.setup_smu(4,smu_ground) + device.setup_var1(parameters) + + device.display_variable("X","V2") + device.display_variable("Y1",'I2') + + device.range_mode(4,"AUTO") + device.range_mode(2,"AUTO") + + device.setup_pulse(parameters) + + device.integration_time(dictionary["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_values("I2") + V_i=device.return_values("V2") + R_i = np.divide(V_i,I_i) + + + expected_time = dictionary["period"].value*dictionary["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") + + display(fig) + +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(dictionary,device): + smu_v = device.smu_dict() + smu_ground = device.smu_dict() + sweep_params = device.var1_dict() + smu_help = device.smu_dict() #this is the uncontacted smu + + smu_v.update( + iname = 'I2', + vname = 'V2', + mode = 'V', + func = 'CONS' + ) + smu_help.update( + iname = 'I3', + vname = 'V3', + mode = 'VPULSE', + func = 'VAR1' + ) + smu_ground.update( + iname ='I4', + vname = 'V4', + mode = 'COMM', + func = 'CONS' + ) + sweep_params.update( + mode ='SING', + start = 0, + stop = 10, + step = 10/(dictionary["pulses"].value-1), #define the number of steps given specific pulses + comp = 0.1, + pcomp = 0, + base = dictionary["base"].value, + width = dictionary["width"].value, + period= dictionary["period"].value + ) + #the constant smu + cons = { + 'value':dictionary["voltage"].value, + 'comp':dictionary["comp"].value + } + + device.measurement_mode("SWE") + device.smu_disable(1) + device.setup_smu(2,smu_v) + device.setup_smu(3,smu_help) + device.setup_smu(4,smu_ground) + device.setup_var1(sweep_params) + device.setup_pulse(sweep_params) + device.setup_cons_smu(2,cons) + + device.user_function('R','OHM','V2/I2') + + device.display_variable("X","@INDEX") + device.display_variable("Y1",'R') + + device.range_mode(4,"AUTO") + device.range_mode(2,"AUTO") + device.range_mode(3,"AUTO") + + + device.integration_time(dictionary["integration"].value) + + device.variables_to_save(['@INDEX','V2','I2','R']) + + t0 = time.time() + device.single_measurement() + while device.operation_completed()== False: + pass + + t1 = time.time() + # get the execution time + elapsed_time = t1 - t0 + + + I_i=device.return_values("I2") + V_i=device.return_values("V2") + R_i = device.return_values('R') + + + expected_time = dictionary["period"].value*dictionary["pulses"].value + + times = (elapsed_time,expected_time) + values = (V_i,I_i,R_i) + + device.del_user_functions() + device.autoscaling() + + 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") + + display(fig) + +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 (Version 4.1)/memristor.py b/hp4155/memristor (Version 4.1)/memristor.py new file mode 100644 index 0000000000000000000000000000000000000000..a42a905898506903fc6655e8024a966a10651247 --- /dev/null +++ b/hp4155/memristor (Version 4.1)/memristor.py @@ -0,0 +1,692 @@ +### 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 +import configparser + +# pulsed libraries +from help_pulse import * + +#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'}, + ) + + +#choose a new folder button +new_folder = widgets.Button(description='change folder') + +image = widgets.Image( + value=open("schematic.png", "rb").read(), + format='png', + width=300, + height=100, +) + +contact_check = widgets.Button(description = 'CONTACT CHECK') +qcc = widgets.Button(description = 'QUICK CONTACT CHECK',layout=widgets.Layout(width='80%'),style={"button_width": "auto"}) +qcc_select = widgets.RadioButtons(description = 'QCC type:',options = ['EBL','OL']) + +vertical1 = widgets.VBox([sample_series,field,DUT,new_folder,contact_check,qcc,qcc_select]) +vertical2 = widgets.VBox([image]) +all_text_boxes = widgets.HBox([vertical1,vertical2]) + + + +#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') + +auto_qcc = widgets.Checkbox( + description = 'Auto QCC after Reset', + style = {'description_width': 'initial'}, + value = True +) + +# 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') +export_ini_button = widgets.Button(description = 'Export as ini') +import_ini_button = widgets.Button(description='Import from ini') + + + +#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):', +) + +# for automatic stop of endurance +auto_stop = widgets.Checkbox( + description = 'Auto QCC after Reset', + style = {'description_width': 'initial'}, + value = False +) + +threshold = widgets.FloatText( + description = "Stop Condition: R(HRS)/R(LRS)<", + style = {'description_width': 'initial'} + value = 1000 +) + +#align a button with a checkbox or integer bounded texts horizontaly +line0 = widgets.HBox([step,integration_time,sampling,auto_qcc]) +line1 = widgets.HBox([set,Vset,CC_vset]) +line2 = widgets.HBox([reset,Vreset,CC_vreset]) +line3 = widgets.HBox([full,number,auto_stop,threshold]) +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() + + +#display all at the end +display(all_text_boxes) + +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) +display(widgets.HBox([import_ini,button,export_ini_button])) +display(output) + +all_widgets=[sweep_button,cons_button,sample_series,field,DUT,set,reset,full,new_folder,retention_button,contact_check,qcc,qcc_select,Vset,CC_vset,Vreset,CC_vreset,step,integration_time,number,sampling,Vretention,period,duration,auto_qcc,auto_stop,threshold,export_ini_button,import_ini_button] +add_widgets_to_list(cons_dict,all_widgets) +add_widgets_to_list(sweep_dict,all_widgets) + +# The regular dictionary for ini +regular_parameters={ + 'step': step, + 'integration': integration, + 'set_voltage': Vset, + 'set_compliance': CC_vset, + 'reset_voltage': Vreset, + 'reset_compliance':CC_vreset, + 'number_of_cycles': number, + 'threshold':threshold, + 'retention_voltage':Vretention, + 'retention_period': period, + 'retention_duration':duration +} + +device = hp4155a.HP4155a('GPIB0::17::INSTR') +device.reset() +device.disable_not_smu() + +#choose folder directory +folder=choose_folder() #here buttons dont work yet! + +def on_contact_check_clicked(b): + global folder,temp_file + with output: + clear_output() + change_state(all_widgets) + device.inst.lock_excl() + + filename=f"{sample_series.value}_{field.value}_{DUT.value}.txt" + file = os.path.join(folder,filename) + + R = regular_contact_check(device) + date = str(datetime.today().replace(microsecond=0)) + title = [f"Full Contact Check at {date}"] + + write_to_file(temp_file,title,R) + + #upload results + temp_file,file,folder=upload_results(temp_file,file,folder) + + information_box("Contact Check Completed") + device.inst.unlock() + + change_state(all_widgets) + +def on_qcc_clicked(b): + global folder,temp_file + with output: + clear_output() + change_state(all_widgets) + device.inst.lock_excl() + + filename=f"{sample_series.value}_{field.value}_{DUT.value}.txt" + file = os.path.join(folder,filename) + device.inst.lock_excl() + + if qcc_select.value == 'EBL': + R = EBL(device) + else: # OL + R = OL(device) #df + + date = str(datetime.today().replace(microsecond=0)) + title = [f"Quick Contact Check ({qcc_select.value}) at {date}"] + + write_to_file(temp_file,title,R) + + #upload results + temp_file,file,folder=upload_results(temp_file,file,folder) + + information_box("Quick Contact Check Completed") + + device.inst.unlock() + + change_state(all_widgets) + + +def on_set_button_clicked(b): + global folder,temp_file + with output: + #disable buttons + change_state(all_widgets) + + filename=f"{sample_series.value}_{field.value}_{DUT.value}.txt" + file = os.path.join(folder,filename) + + #lock the device + device.inst.lock_excl() + + clear_output() + + #check values + valid = check_values(step.value,Vset.value,Vreset.value) + + + if valid == True: + if sampling.value == True: #do sampling set before set process(100mV) + R_mean_before= sampling_check(-0.01,device) + + + #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) + display(df) + + + if sampling.value == True: #do sampling set after set process(10mV) + R_mean_after = sampling_check(0.01,device) + + date = str(datetime.today().replace(microsecond=0)) + title = [f"SET Memristor at {date}",f"Set Voltage={Vset.value}V",f"current compliance={CC_vset.value}A"] + if sampling.value == True: + title.extend([f"R(Ohm) Before/After",f"{R_mean_before} {R_mean_after}"]) + 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(all_widgets) + +def on_reset_button_clicked(b): + global folder,temp_file + with output: + change_state(all_widgets) + + filename=f"{sample_series.value}_{field.value}_{DUT.value}.txt" + file = os.path.join(folder,filename) + + #lock device + device.inst.lock_excl() + + clear_output() + + #check values + valid = check_values(step.value,Vset.value,Vreset.value) + + + if valid == True: + if sampling.value == True: #do sampling set before reset process(10mV) + R_mean_before = sampling_check(0.01,device) + + #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) + display(df) + + if sampling.value == True: #do sampling set after reset process(100mV) + R_mean_after = sampling_check(-0.01,device) + + date = str(datetime.today().replace(microsecond=0)) + title =[f"RESET Memristor at {date}",f"Reset Voltage={Vreset.value}V",f"current compliance={CC_vreset.value}A"] + if sampling.value == True: + title.extend([f"R(Ohm) Before/After",f"{R_mean_before} {R_mean_after}"]) + write_to_file(temp_file,title,df) + + #Quick Contact Check after reset Process + if auto_qcc.value == True: + if qcc_select.value == 'EBL': + R=EBL(device) + else: # OL + R=OL(device) + + title = [f"Automatic Quick Contact Check({qcc_select.value}) after Reset"] + write_to_file(temp_file,title,R) + + #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(all_widgets) + +def on_full_button_clicked(b): + global folder,temp_file + with output: + change_state(all_widgets) + + filename=f"{sample_series.value}_{field.value}_{DUT.value}.txt" + file = os.path.join(folder,filename) + + # lock device + device.inst.lock_excl() + + clear_output() + + #check values + valid = check_values(step.value,Vset.value,Vreset.value) + date = str(datetime.today().replace(microsecond=0)) + + if valid == True: + with open(temp_file,'a') as f: + header =[f"{number.value} full sweeps with parameters:",f"Set Voltage = {Vset.value}V",f"Current compliance set = {CC_vset.value}A",f"Reset Voltage = {Vreset.value}V",f"Current compliance reset = {CC_vreset.value}A"] + + 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 + clear_output(wait = True) + if i>0: + display(fig) + if sampling.value == True: #before set(100mv) + R_mean_init = sampling_check(-0.01,device) + resistances.append(R_mean_init) + + V12,I12 = sweep(0,Vset.value,step.value,CC_vset.value,integration_time.value,device) #set + plot_sweep(V12,I12,f"SET Iteration {i+1}") + + #after set/before reset + if sampling.value == True: #before set(10mv) + R_mean_set = sampling_check(0.01,device) # Here HRS + resistances.append(R_mean_set) + if auto_stop.value == True and abs(R_mean_set/R_mean_init)< abs(threshold).value: + stop = True + + V34,I34 = sweep(0,Vreset.value,step.value,CC_vreset.value,integration_time.value,device) #reset + plot_sweep(V34,I34,f"RESET Iteration {i+1}") + + + #after reset + if sampling.value == True:#-0.1V + R_mean_reset = sampling_check(-0.01,device) #here LRS + resistances.append(R_mean_reset) + if auto_stop.value == True and abs(R_mean_set/R_mean_reset)< abs(threshold).value: + stop = True + + #Quick Contact Check after reset Process + if auto_qcc.value == True: + if qcc_select.value == 'EBL': + R = EBL(device) + else: # OL + R = OL(device) + + #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) + display(df) + if i == 0 : + header.extend([f"{i+1} Iteration"]) + title = header.copy() + else: + title = [f"{i+1} Iteration"] + if sampling.value == True: + title.extend([f"R(Ohm) INIT/SET/RESET",f"{R_mean_init} {R_mean_set} {R_mean_reset}"]) + + write_to_file(temp_file,title,df) + + if auto_qcc.value == True: + title= [f"Quick Contact Check({qcc_select.value}) after Reset"] + write_to_file(temp_file,title,R) + + #plot results + ax1.plot(V,I) + ax2.plot(V,np.absolute(I)) + fig.tight_layout() + + #check for loop termination + if stop == True: + clear_output(wait= True) + time.sleep(2) + display(fig) + information_box("Endurance stopped after esc (manually) or automatically!") + f.write("endurance stopped!\n\n") + break + else: + clear_output(wait = True) + time.sleep(2) + display(fig) + 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.absolute(resistances) + + fig, ax = plt.subplots() + + fig.suptitle('Average Resistances from sampling checks') + ax.set(xlabel='Index',ylabel='Resistance(Ohm)',yscale='log') + ax.scatter(indexes,resistances) + display(fig) + + + #upload results + temp_file,file,folder=upload_results(temp_file,file,folder) + + #unlock the device + device.inst.unlock() + change_state(all_widgets) + + +#new_folder clicked +def on_new_folder_button_clicked(b): + global folder + with output: + change_state(all_widgets) + + folder = choose_folder() #choose new folder + + change_state(all_widgets) + +def on_retention_button_clicked(b): + global folder,temp_file + with output: + + change_state(all_widgets) + + + device.inst.lock_excl() + + clear_output() + + filename=f"{sample_series.value}_{field.value}_{DUT.value}.txt" + file = os.path.join(folder,filename) + + #execute measurement + t,R=retention(Vretention.value,period.value,duration.value,device) + plot_retention(t,R) + df=create_retention_data_frame(t,R) + date = str(datetime.today().replace(microsecond=0)) + title =[f"Retention Memristor at {date}",f"Voltage={Vretention.value}V",f"period={period.value}s",f"duration={duration.value}s"] + + 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() + + change_state(all_widgets) + + +def on_sweep_button_clicked(b): + with output: + clear_output() + change_state(all_widgets) + check_pulse(sweep_dict) + + sample_dict= { + 'series':sample_series, + 'field':field, + 'dut':DUT + } + + times,values = sweep_meas(sweep_dict,device) + plot_sweep_pulse(values) + save_sweep(folder,sample_dict,values,times,sweep_dict) + change_state(all_widgets) + + +def on_constant_button_clicked(b): + with output: + global first + clear_output() + change_state(all_widgets) + + check_pulse(sweep_dict) + + sample_dict= { + 'series':sample_series, + 'field':field, + 'dut':DUT + } + + times,values = constant_meas(cons_dict,device) + plot_constant_pulse(values) + save_constant(folder,sample_dict,values,times,cons_dict) + change_state(all_widgets) + +def on_export_ini_clicked(b): + with output: + change_state(all_widgets) + config = configparser.ConfigParser() + default_filename = 'memristor.ini' + try: + file = save_as_ini(default_filename) + with open(file,'w') as configfile: + config.add_section('ALL VALUES ARE IN SI-UNITS!') + config.add_section('IT IS RECOMMENDED TO CHANGE THE INI FILE FROM THE INTERFACE AND DO NOT CHANGE ANY VALUES MANUALLY') + + #Regular Parameters + config.add_section('Set-Reset-Endurance-Retention') + for parameter,widget in regular_parameters.items(): + config.set('Set-Reset-Endurance-Retention',parameter,str(widget.value)) + + # Sweep_pulse + config.add_section('Sweep Pulse') + for parameter,widget in sweep_dict.items(): + config.set('Sweep Pulse',parameter,str(widget.value)) + + # Constant Pulse + config.add_section('Constant Pulse') + for parameter,widget in cons_dict.items(): + config.set('Constant Pulse',parameter,str(widget.value)) + + config.write(configfile) + except Exception as e: + information_box(e) + + change_state(all_widgets) + +def on_import_ini_clicked(b): + with output: + disable_widgets(all_widgets) + #load values to the interface + config = configparser.ConfigParser() + try: + file = load_ini() + except Exception as e: + information_box(e) + enable_widgets(all_widgets) + return + + try: + #read the values from each section + config.read(file) + + #Regular + for parameter,widget in regular_parameters.items(): + widget.value = config.get('Set-Reset-Endurance-Retention',parameter) + + #Sweep Pulse + for parameter,widget in sweep_dict.items(): + widget.value = config.get('Sweep Pulse',parameter) + for parameter,widget in cons_dict.items(): + widget.value = config.get('Constant Pulse',parameter) + + information_box("all parameters loaded succesfully") + except Exception as error: + if type(error).__name__ =='NoSectionError': + information_box(f"{error}.Explanation: Section(header) [section] does not exist. Create a new ini file or compare it with functional ini files!") + elif type(error).__name__=='NoOptionError': + information_box(f'{error}.Explanation: The variable name before the equal sign is not recognized. Create a new ini file or compare it with functional ini files!') + elif type(error).__name__ == 'TraitError': + information_box(f'{error}.Explanation: Invalid Parameter Setting. Check if you set an invalid value!') + elif type(error).__name__ =="DuplicateOptionError": + information_box(f"{error}. Explaination: The section contains the setted parameter more than once!") + else: + information_box(f"A {type(error).__name__} has occurred. Create A new ini file") + change_state(all_widgets) + + +#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_folder.on_click(on_new_folder_button_clicked) +retention_button.on_click(on_retention_button_clicked) +contact_check.on_click(on_contact_check_clicked) +qcc.on_click(on_qcc_clicked) + +import_ini_button.on_click(on_import_ini_clicked) +export_ini_button.on_click(on_export_ini_clicked) diff --git a/hp4155/memristor (Version 4.1)/memristor_buttons.ipynb b/hp4155/memristor (Version 4.1)/memristor_buttons.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..c9aa7e1d764af70e814dd3f28e2a6ef48ee6801e --- /dev/null +++ b/hp4155/memristor (Version 4.1)/memristor_buttons.ipynb @@ -0,0 +1,86 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "b913930d-b120-4e59-8a42-d9eecb526a61", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "04df3def923445eb95f28afa67430db0", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(VBox(children=(Text(value='', description='sample series:', placeholder='Enter text here:', sty…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "0eeebb7ea7f64c97814087e1c195f32b", + "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": "ff84a98e2896443cbf3470c994e9b383", + "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": "1047e606-d5cb-420b-892f-766226339854", + "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 +} diff --git a/hp4155/memristor (Version 4.1)/schematic.png b/hp4155/memristor (Version 4.1)/schematic.png new file mode 100644 index 0000000000000000000000000000000000000000..7a3a144bfd6c17374838b2d9222e57173fe2fcd8 Binary files /dev/null and b/hp4155/memristor (Version 4.1)/schematic.png differ