From bff9c825090c8c2507cc734444ab8f02003a1cb6 Mon Sep 17 00:00:00 2001
From: unknown <asoalexandros@gmail.com>
Date: Wed, 11 Dec 2024 12:19:09 +0100
Subject: [PATCH] Memristor ini and Automatic stop of endurance!

---
 hp4155/memristor (Version 4.1)/help.py        | 559 ++++++++++++++
 hp4155/memristor (Version 4.1)/help_pulse.py  | 463 ++++++++++++
 hp4155/memristor (Version 4.1)/memristor.py   | 692 ++++++++++++++++++
 .../memristor_buttons.ipynb                   |  86 +++
 hp4155/memristor (Version 4.1)/schematic.png  | Bin 0 -> 17440 bytes
 5 files changed, 1800 insertions(+)
 create mode 100644 hp4155/memristor (Version 4.1)/help.py
 create mode 100644 hp4155/memristor (Version 4.1)/help_pulse.py
 create mode 100644 hp4155/memristor (Version 4.1)/memristor.py
 create mode 100644 hp4155/memristor (Version 4.1)/memristor_buttons.ipynb
 create mode 100644 hp4155/memristor (Version 4.1)/schematic.png

diff --git a/hp4155/memristor (Version 4.1)/help.py b/hp4155/memristor (Version 4.1)/help.py
new file mode 100644
index 0000000..18e87e7
--- /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 0000000..a519559
--- /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 0000000..a42a905
--- /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 0000000..c9aa7e1
--- /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
GIT binary patch
literal 17440
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

literal 0
HcmV?d00001

-- 
GitLab