diff --git a/hp4155/ADU_for_double_gate_devices_version_3/double_gate_ADU.py b/hp4155/ADU_for_double_gate_devices_version_3/double_gate_ADU.py new file mode 100644 index 0000000000000000000000000000000000000000..c7d8946a74df40248e03357beffb540890e8e3dd --- /dev/null +++ b/hp4155/ADU_for_double_gate_devices_version_3/double_gate_ADU.py @@ -0,0 +1,264 @@ +import sys +sys.path.insert(0, './lib') +sys.path.insert(0, '..') #append parent directory + + +import hp4155a +from interface import * +from help import * +import transfer +import output +import gatediode +import configparser + + +#setup user interface +ui = interface() +device = hp4155a.HP4155a('GPIB0::17::INSTR') + +def on_start_clicked(b): + with ui.output: + clear_output() + #disable all widgets + change_state(ui.all_widgets) + + #additional code to check smu configuration fast without modifing a lot of code + try: + check_configuration(ui.sample) + except Exception as e: + error_box(e) + change_state(ui.all_widgets) + return + + #setup the devicr + device.reset() + + #setup sweep measurement mode + device.measurement_mode('SWE') + + #disable all irrelevant units + device.disable_not_smu() + + if ui.transfer_check.value == True: + if ui.transfer_gates.value == 'VTG': + transfer.VTG(ui,device) + elif ui.transfer_gates.value == 'VBG': + transfer.VBG(ui,device) + elif ui.transfer_gates.value == 'BOTH SIMULTANEOUSLY': + transfer.SIM(ui,device) + else: # Sequential measurement + transfer.SEQ(ui,device) + + if ui.output_check.value == True: + if ui.output_gates.value == "VTG": + output.VTG(ui,device) + elif ui.output_gates.value == "VBG": + output.VBG(ui,device) + else: # Both + output.SEQ(ui,device) + + + if ui.gatediode_check.value == True: + if ui.gatediode_gates.value == 'VTG': + gatediode.VTG(ui,device) + else: #VBG + gatediode.VBG(ui,device) + + information_box("Measurement finished!") + change_state(ui.all_widgets) + +def on_export_ini_clicked(b): + with ui.output: + change_state(ui.all_widgets) + config = configparser.ConfigParser() + default_filename = 'ADU_double_gate.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') + + # Transfer curve + config.add_section('Transfer') + config.set('Transfer','Integration',ui.integration_transfer.value) + + config.add_section("Vtg_transfer") + for parameter, widget in vars(ui.Vtg_transfer).items(): + if parameter != "grid": + config.set("Vtg_transfer",parameter,str(widget.value)) + + + config.add_section("Vbg_transfer") + for parameter,widget in vars(ui.Vbg_transfer).items(): + if parameter != "grid": + config.set('Vbg_transfer',parameter,str(widget.value)) + + config.add_section('Vds_transfer') + for parameter,widget in vars(ui.Vds_transfer).items(): + if parameter != "grid": + config.set('Vds_transfer',parameter,str(widget.value)) + + + config.add_section('Plot_transfer') + for parameter,widget in vars(ui.plot_transfer).items(): + if parameter != "grid": + config.set('Plot_transfer',parameter,str(widget.value)) + + #output + config.add_section('Output') + config.set('Output','Integration',ui.integration_output.value) + + config.add_section("Vtg_output") + for parameter,widget in vars(ui.Vtg_output).items(): + if parameter != "grid": + config.set('Vtg_output',parameter,str(widget.value)) + + + config.add_section("Vbg_output") + for parameter,widget in vars(ui.Vbg_output).items(): + if parameter != "grid": + config.set('Vbg_output',parameter,str(widget.value)) + + + config.add_section('Vds_output') + for parameter,widget in vars(ui.Vds_output).items(): + if parameter != "grid": + config.set('Vds_output',parameter,str(widget.value)) + + + config.add_section('Plot_output') + for parameter,widget in vars(ui.plot_output).items(): + if parameter != "grid": + config.set('Plot_output',parameter,str(widget.value)) + + # Gatediode + config.add_section('Gatediode') + config.set('Gatediode','Integration',ui.integration_gatediode.value) + + config.add_section("Vg_gatediode") + for parameter,widget in vars(ui.Vg_gatediode).items(): + if parameter != "grid": + config.set('Vg_gatediode',parameter,str(widget.value)) + + config.add_section('Plot_gatediode') + for parameter,widget in vars(ui.plot_gatediode).items(): + if parameter != "grid": + config.set('Plot_gatediode',parameter,str(widget.value)) + + # add the smu configuration + config.add_section('SMU_configuration') + config.set("SMU_configuration","D",str(ui.sample.drain.value)) + config.set("SMU_configuration","TG",str(ui.sample.top_gate.value)) + config.set("SMU_configuration","BG",str(ui.sample.back_gate.value)) + config.set("SMU_configuration","S",str(ui.sample.source.value)) + + # Add the sweeping gates + config.add_section('Sweeping_gates') + config.set('Sweeping_gates','Transfer',str(ui.transfer_gates.value)) + config.set('Sweeping_gates','Output',str(ui.output_gates.value)) + config.set('Sweeping_gates','Gatediode',str(ui.gatediode_gates.value)) + + + config.write(configfile) + except Exception as e: + information_box(e) + + change_state(ui.all_widgets) + + +def on_import_ini_clicked(b): + with ui.output: + change_state(ui.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) + + #transfer curve + ui.integration_transfer.value = config.get('Transfer', "integration") + for parameter,widget in vars(ui.Vtg_transfer).items(): + if parameter != "grid": + widget.value = config.get('Vtg_transfer',parameter) + for parameter,widget in vars(ui.Vds_transfer).items(): + if parameter != "grid": + widget.value = config.get('Vds_transfer',parameter) + for parameter,widget in vars(ui.Vbg_transfer).items(): + if parameter != "grid": + widget.value = config.get('Vbg_transfer',parameter) + for parameter,widget in vars(ui.plot_transfer).items(): + if parameter != "grid": + widget.value = config.get('Plot_transfer',parameter) + + #output curve + ui.integration_output.value = config.get('Output','integration') + for parameter,widget in vars(ui.Vtg_output).items(): + if parameter != "grid": + widget.value = config.get('Vtg_output',parameter) + for parameter,widget in vars(ui.Vds_output).items(): + if parameter != "grid": + widget.value = config.get('Vds_output',parameter) + for parameter,widget in vars(ui.Vbg_output).items(): + if parameter != "grid": + widget.value = config.get('Vbg_output',parameter) + for parameter,widget in vars(ui.plot_output).items(): + if parameter != "grid": + widget.value = config.get('Plot_output',parameter) + + # gatediode + ui.integration_gatediode.value = config.get('Gatediode','integration') + for parameter,widget in vars(ui.Vg_gatediode).items(): + if parameter != "grid": + widget.value = config.get('Vg_gatediode',parameter) + + for parameter,widget in vars(ui.plot_gatediode).items(): + if parameter != "grid": + widget.value = config.get('Plot_gatediode',parameter) + + # SMU map + ui.sample.drain.value = int(config.get("SMU_configuration","d")) + ui.sample.top_gate.value = int(config.get("SMU_configuration","tg")) + ui.sample.back_gate.value = int(config.get("SMU_configuration","bg")) + ui.sample.source.value = int(config.get("SMU_configuration","s")) + + # sweeping gates + ui.transfer_gates.value = config.get('Sweeping_gates','Transfer') + ui.output_gates.value = config.get('Sweeping_gates','Output') + ui.gatediode_gates.value = config.get('Sweeping_gates','Gatediode') + + 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(ui.all_widgets) + + +ui.start.on_click(on_start_clicked) +ui.import_ini.on_click(on_import_ini_clicked) +ui.export_ini.on_click(on_export_ini_clicked) + + + + + + + + + + + diff --git a/hp4155/ADU_for_double_gate_devices_version_3/double_gate_ADU_interface.ipynb b/hp4155/ADU_for_double_gate_devices_version_3/double_gate_ADU_interface.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..bccf7119845f923c3f56960e8137605718e24d62 --- /dev/null +++ b/hp4155/ADU_for_double_gate_devices_version_3/double_gate_ADU_interface.ipynb @@ -0,0 +1,114 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "51b012d0-95b0-41c2-81bb-2205f3c53be2", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "0879807dae214b789264fdcd4c264999", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(VBox(children=(Label(value='Sample Information', layout=Layout(height='auto', width='50%'), sty…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "6d4ea5ad1b6143a4ae91ee17406184bf", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(Checkbox(value=True, description='Transfer', indent=False), Checkbox(value=True, description='O…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "92b167e872da4970ac54d2be519e3f98", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Tab(children=(VBox(children=(Dropdown(description='Integration Time', index=1, layout=Layout(height='auto', wi…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "3d99607bed9d4b9e886dad6d5a98062e", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(Button(description='Start Measurement', style=ButtonStyle()), Button(description='Import from i…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "e6f9253c0a1b41b6ad3476cd030f6b8f", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Output()" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%run double_gate_ADU.py" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3d0c5404-a1c6-40f9-bffd-625199c945a7", + "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/ADU_for_double_gate_devices_version_3/lib/gatediode.py b/hp4155/ADU_for_double_gate_devices_version_3/lib/gatediode.py new file mode 100644 index 0000000000000000000000000000000000000000..68352d61d34c4e5b19940ca1c8535b970753c455 --- /dev/null +++ b/hp4155/ADU_for_double_gate_devices_version_3/lib/gatediode.py @@ -0,0 +1,186 @@ +# This are the gatediode measurements + +import sys +sys.path.insert(0, '..') #append parent directory + +import hp4155a +from help import * +from decimal import Decimal + +import os + +def VTG(ui,device): + device.del_user_functions() # delete all user functions + device.clear_error_stack() # clear error stack + # setup the smus + + norm = normalization_factor(ui.sample.width.value) + + smu_source = device.smu_dict() + smu_source.update(vname ='VS',iname = 'IS',mode = 'COMM',func='CONS') + + smu_top_gate = device.smu_dict() + smu_top_gate.update(vname = 'VTG',iname='ITG',mode = 'V',func='VAR1') + + # setup VAR1 + var1 = device.var1_dict() + var1.update( + mode = ui.Vg_gatediode.hyst.value, + start = ui.Vg_gatediode.start.value, + stop = ui.Vg_gatediode.stop.value, + step = ui.Vg_gatediode.step.value, + comp = ui.Vg_gatediode.comp.value, + pcomp = ui.Vg_gatediode.pcomp.value + ) + + try: + device.setup_smu(ui.sample.top_gate.value,smu_top_gate) + device.setup_smu(ui.sample.source.value,smu_source) + + # disable back gate smu + device.smu_disable(ui.sample.back_gate.value) + + # disable drain smu + device.smu_disable(ui.sample.drain.value) + + device.setup_var1(var1) + device.integration_time(ui.integration_gatediode.value) + + variables_list = ['VTG','ITG'] + device.variables_to_save(variables_list) + + plotted_variables = graph_tool(ui.plot_gatediode,ui.sample.width.value,device) + + device.error_occured() + + device.single_measurement() + while device.operation_completed()==False: + pass + + device.error_occured() + + if ui.sample.quick.value == False: + device.autoscaling() + + + values = dict([(variable,device.return_values(variable)) for variable in variables_list]) + df = get_dataframe_from_results(values) + except Exception as e: + error_box(e) + return + + # Append the normalized current + df["ITGmm/uA/um"]= (df["ITG/A"].apply(lambda x: Decimal(str(x))*Decimal(str(norm)))).astype('float') + default_filename = f"{ui.sample.sample.value}_{ui.sample.field.value}_{ui.sample.device.value}_TOP_GATE_D.txt" + file = create_file(default_filename) + + with open(file,'w') as f: + date = str(datetime.today().replace(microsecond=0)) + f.write(f"Gatediode Curve at {date}"+"\n") + write_sample_information(ui.sample,f) + f.write("Sweeping Gate:VTG"+"\n\n") + f.write('Parameters\n') + f.write(f"VTG from {ui.Vg_gatediode.start.value}V to {ui.Vg_gatediode.stop.value}V with step {ui.Vg_gatediode.step.value}V"+"\n") + + if ui.Vg_gatediode.pcomp.value==0: + f.write(f"Top Gate Current Compliance/A:{ui.Vg_gatediode.comp.value}"+"\n") + else: + f.write(f"Top Gate Power Compliance/A:{ui.Vg_gatediode.pcomp.value}"+"\n") + + f.write(f"Integration Time:{ui.integration_gatediode.value}"+"\n") + f.write("\nResults\n") + + df.to_csv(file,sep=" ",mode='a') + + if ui.sample.quick.value == False: + df["label"] = df.apply(lambda row: "_nolegend_",axis = 1) # assign labels + create_plot(ui.plot_gatediode,df,file,"Top Gate Gatediode Measurement",None,ui.sample.save_fig.value) + +def VBG(ui,device): + device.del_user_functions() # delete all user functions + device.clear_error_stack() # clear error stack + # setup the smus + + norm = normalization_factor(ui.sample.width.value) + + smu_source = device.smu_dict() + smu_source.update(vname ='VS',iname = 'IS',mode = 'COMM',func='CONS') + + smu_back_gate = device.smu_dict() + smu_back_gate.update(vname = 'VBG',iname='IBG',mode = 'V',func='VAR1') + + # setup VAR1 + var1 = device.var1_dict() + var1.update( + mode = ui.Vg_gatediode.hyst.value, + start = ui.Vg_gatediode.start.value, + stop = ui.Vg_gatediode.stop.value, + step = ui.Vg_gatediode.step.value, + comp = ui.Vg_gatediode.comp.value, + pcomp = ui.Vg_gatediode.pcomp.value + ) + + try: + device.setup_smu(ui.sample.top_gate.value,smu_back_gate) + device.setup_smu(ui.sample.source.value,smu_source) + + # disable back gate smu + device.smu_disable(ui.sample.back_gate.value) + + # disable drain smu + device.smu_disable(ui.sample.drain.value) + + device.setup_var1(var1) + device.integration_time(ui.integration_gatediode.value) + + variables_list = ['VBG','IBG'] + device.variables_to_save(variables_list) + + plotted_variables = graph_tool(ui.plot_gatediode,ui.sample.width.value,device) + + device.error_occured() + + device.single_measurement() + while device.operation_completed()==False: + pass + + device.error_occured() + + if ui.sample.quick.value == False: + device.autoscaling() + + + values = dict([(variable,device.return_values(variable)) for variable in variables_list]) + df = get_dataframe_from_results(values) + except Exception as e: + error_box(e) + return + + # Append the normalized current + df["IBGmm/uA/um"]= (df["IBG/A"].apply(lambda x: Decimal(str(x))*Decimal(str(norm)))).astype('float') + default_filename = f"{ui.sample.sample.value}_{ui.sample.field.value}_{ui.sample.device.value}_BACK_GATE_D.txt" + file = create_file(default_filename) + + with open(file,'w') as f: + date = str(datetime.today().replace(microsecond=0)) + f.write(f"Gatediode Curve at {date}"+"\n") + write_sample_information(ui.sample,f) + f.write("Sweeping Gate:VBG"+"\n\n") + f.write('Parameters\n') + f.write(f"VTG from {ui.Vg_gatediode.start.value}V to {ui.Vg_gatediode.stop.value}V with step {ui.Vg_gatediode.step.value}V"+"\n") + + if ui.Vg_gatediode.pcomp.value==0: + f.write(f"Top Gate Current Compliance/A:{ui.Vg_gatediode.comp.value}"+"\n") + else: + f.write(f"Top Gate Power Compliance/A:{ui.Vg_gatediode.pcomp.value}"+"\n") + + f.write(f"Integration Time:{ui.integration_gatediode.value}"+"\n") + f.write("\nResults\n") + + df.to_csv(file,sep=" ",mode='a') + + if ui.sample.quick.value == False: + df["label"] = df.apply(lambda row: "_nolegend_",axis = 1) # assign labels + create_plot(ui.plot_gatediode,df,file,"Top Gate Gatediode Measurement",None,ui.sample.save_fig.value) + + \ No newline at end of file diff --git a/hp4155/ADU_for_double_gate_devices_version_3/lib/help.py b/hp4155/ADU_for_double_gate_devices_version_3/lib/help.py new file mode 100644 index 0000000000000000000000000000000000000000..40f602d3486fafb1df850701baf00481da19d4e2 --- /dev/null +++ b/hp4155/ADU_for_double_gate_devices_version_3/lib/help.py @@ -0,0 +1,291 @@ +import matplotlib.pyplot as plt +import numpy as np +import time +from datetime import datetime + +import tkinter as tk +from tkinter import filedialog +import tkinter.messagebox +import copy +import os + +import pandas as pd + +#Get dataframe from results +def get_dataframe_from_results(dictionary): + # creating a shallow copy + dictionary_copy = copy.copy(dictionary) + for old_key in dictionary_copy.keys(): + if old_key[0]=='I': + new_key = old_key+"/A" + else: #V + new_key = old_key + "/V" + dictionary[new_key] = dictionary.pop(old_key) + + df = pd.DataFrame(dictionary) + return df + +def number_of_points(obj): # obj is interface object + try: + diff = obj.stop.value - obj.start.value + ratio = abs(diff/obj.step.value) + points = int(ratio+1) + + except ZeroDivisionError: + points = 1 + + #the programm crashed because for secondary step we had no problem setting start = stop and then it was dividing by zero + + if points>128: + points = 128 + return points + + +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() + +def error_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.showerror(message=information) + root.destroy() + +#normalization factor to is for both normalizations 10**6/width mA/mm = uA/um = 10**(-6)A/um (returned from the tool) +def normalization_factor(width): + factor = 10**6/width + return factor + + +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 + +# function to return ratio and offset for synchronous sweep measurement +def calculate_line(VTG,VBG): # from now interface objects + ratio = (VBG.stop.value-VBG.start.value)/(VTG.stop.value-VTG.start.value) + offset = VBG.start.value-ratio*VTG.start.value + return ratio,offset + + + +#check if smu configuration has a double value +def check_configuration(sample): # the sample object from the interface + #convert the dictionaries values to a list + + map_list = [sample.source.value,sample.top_gate.value,sample.back_gate.value,sample.drain.value] + + #remove the duplicates by using a set + map_set = set(map_list) + + if len(map_set)!= len(map_list): #we have duplicates + raise Exception("You cannot assign a smu to multiple contacts") + + + +# The function for graph in the tool +def graph_tool(plot_config,width,device): # the plot config object from the interface + device.delete_axis("X") + device.delete_axis("Y1") + device.delete_axis("Y2") + + # How to define user functions correctly and not multiple times + plot_list = [plot_config.x.value,plot_config.y1.value,plot_config.y2.value] + plot_set = set(plot_list) + + + #define the no values + for element in plot_set: + if element != "None": + if element.endswith("mm"): #only I values + #define the respective user function + device.user_function(element,"mA/mm",f"{element[:-2]}*{normalization_factor(width)}") #max 3 + + #define the absolute(A to indicate absolute) always 3 (max 6!) + if element.startswith('I') and element.endswith('mm'): + device.user_function('A'+element,"mA/mm",f"ABS({element})") + elif element.startswith('I') and element.endswith('mm')== False: + device.user_function('A'+element,"A",f"ABS({element})") + + + # Now send the parameters in the tool + device.display_variable('X',plot_config.x.value) + device.axis_scale('X','LIN') + device.display_variable_min_max('X','MIN',plot_config.x_min.value) + device.display_variable_min_max('X','MAX',plot_config.x_max.value) + + + if plot_config.y1_scale.value=='LOG': + device.display_variable('Y1',"A"+plot_config.y1.value) + else: + device.display_variable('Y1',plot_config.y1.value) + device.axis_scale('Y1',plot_config.y1_scale.value) + device.display_variable_min_max('Y1','MIN',plot_config.y1_min.value) + device.display_variable_min_max('Y1','MAX',plot_config.y1_max.value) + + if plot_config.y2.value!= "None": + if plot_config.y2_scale.value=='LOG': + device.display_variable('Y2',"A"+plot_config.y2.value) + else: + device.display_variable('Y2',plot_config.y2.value) + device.axis_scale('Y2',plot_config.y2_scale.value) + device.display_variable_min_max('Y2','MIN',plot_config.y2_min.value) + device.display_variable_min_max('Y2','MAX',plot_config.y2_max.value) + + + +#plot software functions +def column_to_plot(element): + if element != "None": + if element.startswith("V"): + column = element+"/V" + label = f"{element} (V)" + elif element.startswith('I') and element.endswith('mm'): + column = element + "/uA/um" + label = f"{element[:-2]} (uA/um)" + else: # regular I + column = element+"/A" + label = f"{element} (A)" + + return column,label + +def create_file(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=".txt", filetypes=[("Text files","*.txt")],title = "save results path",initialfile =filename) + + #check if the file path is correct(.txt) + while file.endswith(".txt") == False: + #open again filedialog with error message box + tk.messagebox.showerror(message='invalid filename!') + file = filedialog.asksaveasfilename(defaultextension=".txt", filetypes=[("Text files","*.txt")],title = "save results path",initialfile =default_filename) + + root.destroy() + + return file + +def write_sample_information(sample,file): + file.write(f"Series:{sample.processing_number.value}"+"\n") + file.write(f"Sample:{sample.sample.value}"+"\n") + file.write(f"Field:{sample.field.value}"+"\n") + file.write(f"Device:{sample.device.value}"+"\n") + file.write(f"Device Width/um:{sample.width.value}"+"\n") + +def create_plot(plot_config,df,file,fig_title,legend_title,save): + labels = df["label"].unique() + colors = plt.cm.tab20.colors[:len(labels)] + + fig,ax1 = plt.subplots(figsize = (10,6)) + x_col,x_label = column_to_plot(plot_config.x.value) + y1_col, y1_label = column_to_plot(plot_config.y1.value) + + if plot_config.y1_scale.value == 'LOG': + ax1.set_yscale('log') + for color, label in zip(colors,labels): + subset = df[df["label"]== label] + ax1.plot(subset[x_col],subset[y1_col].abs(),color = color,label = label) + else: + for color, label in zip(colors,labels): + subset = df[df["label"]== label] + ax1.plot(subset[x_col],subset[y1_col],color = color,label = label) + + ax1.set_xlabel(x_label) + ax1.set_ylabel(y1_label) + ax1.set_title(fig_title,fontweight = 'bold') + ax1.grid(True) + ax1.legend( + title = legend_title, + bbox_to_anchor = (1.05,1), + loc = "upper left" + ) + + fig.tight_layout() + display(fig) + + if save: + filename = os.path.splitext(file)[0] + fig.savefig(filename+"_Y1.png") + + # now for the y2 axis + if plot_config.y2.value!= "None": + fig,ax2 = plt.subplots(figsize = (10,6)) + y2_col, y2_label = column_to_plot(plot_config.y2.value) + + if plot_config.y2_scale.value == 'LOG': + ax2.set_yscale('log') + for color, label in zip(colors,labels): + subset = df[df["label"]== label] + ax2.plot(subset[x_col],subset[y2_col].abs(),color = color,label = label) + else: + for color, label in zip(colors,labels): + subset = df[df["label"]== label] + ax2.plot(subset[x_col],subset[y2_col],color = color,label = label) + + ax2.set_xlabel(x_label) + ax2.set_ylabel(y2_label) + ax2.set_title(fig_title,fontweight = 'bold') + ax2.grid(True) + ax2.legend( + title = legend_title, + bbox_to_anchor = (1.05,1), + loc = "upper left" + ) + fig.tight_layout() + display(fig) + + if save: + filename = os.path.splitext(file)[0] + fig.savefig(filename+"_Y2.png") \ No newline at end of file diff --git a/hp4155/ADU_for_double_gate_devices_version_3/lib/interface.py b/hp4155/ADU_for_double_gate_devices_version_3/lib/interface.py new file mode 100644 index 0000000000000000000000000000000000000000..c1deb7d1c25e72d4691813be97ef2e4c36089dd5 --- /dev/null +++ b/hp4155/ADU_for_double_gate_devices_version_3/lib/interface.py @@ -0,0 +1,388 @@ +import ipywidgets as widgets +from ipywidgets import GridspecLayout,Layout +from IPython.display import clear_output +import sys +import os + +width = "50%" +height = 'auto' +style = {'description_width': 'initial'} +floatbox_width = "80%" + + +# functionallities for the widgets +# No more dictionaries +def add_widgets_to_list(source_class,target_list): + for widget in (vars(source_class)).values(): + target_list.append(widget) + +def change_state(widgets_list): + for widget in widgets_list: + try: + widget.disabled = not widget.disabled + except: + pass # Perhaps other objects are there too + +# here are the custom widgets +def measurement_check(name): + check=widgets.Checkbox( + description = name, + value = True, + indent = False + ) + + return check + + +def integration_time(selection): + + options_integration=["SHORt","MEDium","LONG"] + + integration= widgets.Dropdown( + options=options_integration, + value=selection,description='Integration Time', + style =style, + layout=Layout(height='auto', width="30%") + ) + + return integration + +def gate_selection(*options): + gates = widgets.Dropdown( + options = options, + description = 'Selected Gate(s):', + style = style + ) + return gates + +# Here are the custom grids +class primary: + def __init__(self,name,start,step,stop,comp): + self.grid = GridspecLayout(4,4) + self.grid[:,3]=widgets.Label(name,layout=Layout(height='auto', width='auto')) + self.grid[:,3].style.font_weight = 'bold' + + + #first line + self.grid[0,0]=widgets.Label("Start(V)",layout=Layout(height='auto', width='auto')) + self.grid[0,1]=widgets.Label("Step(V)",layout=Layout(height='auto', width='auto')) + self.grid[0,2]=widgets.Label("Stop(V)",layout=Layout(height='auto', width='auto')) + + #second line + self.grid[1,0]=widgets.BoundedFloatText(value=start,min=-100,max=100,step=1,layout=Layout(height='auto', width=floatbox_width)) + self.grid[1,1]=widgets.BoundedFloatText(value=step,min=-200,max=200,step=1,layout=Layout(height='auto', width=floatbox_width)) + self.grid[1,2]=widgets.BoundedFloatText(value=stop,min=-100,max=100,step=1,layout=Layout(height='auto', width=floatbox_width)) + + #third line + self.grid[2,0]=widgets.Label("Compliance(A)",layout=Layout(height='auto', width='auto')) + self.grid[2,1] =widgets.Label("Power Compliance(W)(0=OFF)",layout=Layout(height='auto', width='auto'))#mind the gap + self.grid[2,2] =widgets.Label("Hysterisis",layout=Layout(height='auto', width='auto'))#mind the gap + + #fourth line + self.grid[3,0]=widgets.BoundedFloatText(value=comp,min=-0.1,max=0.1,step=0.01,layout=Layout(height='auto', width=floatbox_width)) + self.grid[3,1]=widgets.BoundedFloatText(value=0,min=0,max=2,step=0.1,layout=Layout(height='auto', width=floatbox_width))#mind the gap + self.grid[3,2]=widgets.Dropdown(options=['SINGle','DOUBle'],value='SINGle',layout=Layout(height='auto', width=floatbox_width))#mind the gap + + + self.start = self.grid[1,0] + self.step = self.grid[1,1] + self.stop = self.grid[1,2] + self.comp = self.grid[3,0] + self.hyst = self.grid[3,2] + self.pcomp = self.grid[3,1] + +class secondary: + def __init__(self,name,start,step,stop,comp): + self.grid = GridspecLayout(4,4) + self.grid[:,3]=widgets.Label(name,layout=Layout(height='auto', width='auto')) + self.grid[:,3].style.font_weight = 'bold' + + #first line + self.grid[0,0]=widgets.Label("Start(V)",layout=Layout(height='auto', width='auto')) + self.grid[0,1]=widgets.Label("Step(V)",layout=Layout(height='auto', width='auto')) + self.grid[0,2]=widgets.Label("Stop(V)",layout=Layout(height='auto', width='auto')) + + #second line + self.grid[1,0]=widgets.BoundedFloatText(value=start,min=-100,max=100,step=1,layout=Layout(height='auto', width=floatbox_width)) + self.grid[1,1]=widgets.BoundedFloatText(value=step,min=-200,max=200,step=1,layout=Layout(height='auto', width=floatbox_width)) + self.grid[1,2]=widgets.BoundedFloatText(value=stop,min=-100,max=100,step=1,layout=Layout(height='auto', width=floatbox_width)) + + #third line + self.grid[2,0]=widgets.Label("Compliance(A)",layout=Layout(height='auto', width='auto')) + self.grid[2,2] =widgets.Label("Power Compliance(W)(0=OFF)",layout=Layout(height='auto', width='auto'))#mind the gap + + #fourth line + self.grid[3,0]=widgets.BoundedFloatText(value=comp,min=-0.1,max=0.1,step=0.01,layout=Layout(height='auto', width=floatbox_width)) + self.grid[3,2]=widgets.BoundedFloatText(value=0,min=0,max=2,step=0.1,layout=Layout(height='auto', width=floatbox_width))#mind the gap + + self.start = self.grid[1,0] + self.step = self.grid[1,1] + self.stop = self.grid[1,2] + self.comp = self.grid[3,0] + self.pcomp = self.grid[3,2] + + + + +class sample: + def __init__(self): + width = '90%' + sample_information=widgets.Label("Sample Information",layout=Layout(height=height, width='50%')) + sample_information.style.font_weight='bold' + self.grid=GridspecLayout(3,2) + + for i in range(3): + for j in range(2): + if i ==2 and j == 1: + self.grid[i,j]=widgets.Checkbox(value = True,indent = False) + elif i == 2 and j == 0: + self.grid[i,j]=widgets.BoundedFloatText( + value=100, + min=1e-3, + max=sys.float_info.max,step=1, + layout=Layout(height=height, width=width) + ) + else: + self.grid[i,j]=widgets.Text(layout=Layout(height=height, width=width)) + + self.grid[0,0].description = "Processing-Nr:" + self.grid[1,0].description = "Sample:" + self.grid[2,0].description = "Device Width(um):" + self.grid[0,1].description = "Field(XYY):" + self.grid[1,1].description = "Device:" + self.grid[2,1].description ='Save Plots' + + for i in range(3): + for j in range(2): + self.grid[i,j].style = style + + + + config = widgets.Label("SMU Configuration",layout=Layout(height='auto', width='auto')) + + self.top_gate = widgets.Dropdown(options=[1,2,3,4],value=1,layout=Layout(height='auto', width='auto'),description = 'Top Gate:',style = style) + self.drain = widgets.Dropdown(options=[1,2,3,4],value=2,layout=Layout(height='auto', width='auto'),description = 'Drain:',style = style) + self.back_gate = widgets.Dropdown(options=[1,2,3,4],value=3,layout=Layout(height='auto', width='auto'),description = 'Back Gate:',style = style) + self.source = widgets.Dropdown(options=[1,2,3,4],value=4,layout=Layout(height='auto', width='auto'),description = 'Source(Ground):',style = style) + self.quick = widgets.Checkbox(description = "Quick Measurement",value = False, indent = False) + + + vbox2 = widgets.VBox([config,self.top_gate,self.drain,self.back_gate,self.source,self.quick]) + vbox1=widgets.VBox([sample_information,self.grid]) + display(widgets.HBox([vbox1,vbox2])) + + self.processing_number = self.grid[0,0] + self.sample = self.grid[1,0] + self.field = self.grid[0,1] + self.device = self.grid[1,1] + self.width = self.grid[2,0] + self.save_fig = self.grid[2,1] + + +class plot_config: #meas = 1,2,3 for transfer,output,gatediode + def __init__(self,meas): + + self.grid = GridspecLayout(6,4) + + if meas == 2 : # constraints + options_x = ['VDS'] + else : + options_x= ['VTG','VBG'] + + if meas ==3: + options = ['ITG','IBG','ITGmm','IBGmm','None'] + else: + options = ['ITG','IBG','ID','ITGmm','IBGmm','IDmm','None'] + + # define the default values (y2 is always empty) taken from the interface + if meas == 1:# Transfer + x_name = 'VTG' + x_scale = 'LIN' + x_min = -5 + x_max = 5 + y1_name = 'IDmm' + y1_scale = 'LOG' + y1_min = 0 + y1_max = 100 + + elif meas == 2: # outptut + x_name = 'VDS' + x_scale = 'LIN' + x_min = 0 + x_max = 5 + y1_name = 'IDmm' + y1_scale = 'LIN' + y1_min = -100 + y1_max = 100 + + else: # == 3 gatediode + x_name = 'VTG' + x_scale = 'LIN' + x_min = -5 + x_max = 5 + y1_name = 'ITGmm' + y1_scale = 'LOG' + y1_min = 0 + y1_max = 10 + + + self.grid[0,:]=widgets.Label('Send Plotting configurations to the tool',layout=Layout(height='auto', width='auto')) + #first line headers + self.grid[1,1]= widgets.Label('X',layout=Layout(height='auto', width='auto')) + self.grid[1,2]= widgets.Label('Y1',layout=Layout(height='auto', width='auto')) + self.grid[1,3]= widgets.Label('Y2',layout=Layout(height='auto', width='auto')) + + #first column + self.grid[2,0] = widgets.Label('NAME',layout=Layout(height='auto', width='auto')) + self.grid[3,0] = widgets.Label('SCALE',layout=Layout(height='auto', width='auto')) + self.grid[4,0] = widgets.Label('MIN',layout=Layout(height='auto', width='auto')) + self.grid[5,0] = widgets.Label('MAX',layout=Layout(height='auto', width='auto')) + + #iterate through the second line (NAME) + must_options = [x for x in options if x!='None'] + self.grid[2,1]=widgets.Dropdown(layout=Layout(height='auto', width='auto'),options = options_x,value = x_name) + self.grid[2,2]=widgets.Dropdown(layout=Layout(height='auto', width='auto'),options = must_options,value = y1_name) + self.grid[2,3]=widgets.Dropdown(layout=Layout(height='auto', width='auto'),options = options,value = "None") + + #Iterate through the third line (scale) + options_scale = ['LIN','LOG'] + self.grid[3,1] = widgets.Dropdown(value = x_scale,options = options_scale, layout=Layout(height='auto', width='auto'),disabled = True) + self.grid[3,2] = widgets.Dropdown(value = y1_scale,options = options_scale, layout=Layout(height='auto', width='auto')) + self.grid[3,3] = widgets.Dropdown(value = 'LIN',options = options_scale, layout=Layout(height='auto', width='auto')) + + #iterate throuh the last 2 lines(min-max) + + self.grid[4,1] = widgets.FloatText(value = x_min,layout=Layout(height='auto', width='auto')) #min + self.grid[4,2] = widgets.FloatText(value = y1_min,layout=Layout(height='auto', width='auto')) #min + self.grid[4,3] = widgets.FloatText(value = 0,layout=Layout(height='auto', width='auto')) #min + + self.grid[5,1]=widgets.FloatText(value = x_max,layout=Layout(height='auto', width='auto')) #max X-axis + self.grid[5,2]=widgets.FloatText(value = y1_max,layout=Layout(height='auto', width='auto')) #max Y1-axis + self.grid[5,3]=widgets.FloatText(value = 0,layout=Layout(height='auto', width='auto')) #max Y2-axis + + self.x = self.grid[2,1] + self.y1 = self.grid[2,2] + self.y2 = self.grid[2,3] + self.y1_scale = self.grid[3,2] + self.y2_scale = self.grid[3,3] + self.x_min = self.grid[4,1] + self.y1_min = self.grid[4,2] + self.y2_min = self.grid[4,3] + self.x_max = self.grid[5,1] + self.y1_max = self.grid[5,2] + self.y2_max = self.grid[5,3] + + +# The whole interface +class interface: + def __init__(self): + self.sample = sample() + + self.transfer_check = measurement_check("Transfer") + self.output_check = measurement_check("Output") + self.gatediode_check = measurement_check("Gatediode") + + checkboxes = widgets.HBox([self.transfer_check,self.output_check,self.gatediode_check]) + display(checkboxes) + + #transfer + + self.integration_transfer = integration_time("MEDium") + self.transfer_gates = gate_selection("VTG","VBG","BOTH SIMULTANEOUSLY","BOTH SEQUENTIALLY") + + self.Vds_transfer = secondary('VDS',0.05,0.95,1,1e-2) + self.Vtg_transfer = primary('VTG',-5,0.01,5,1e-3) + self.Vbg_transfer = primary('VBG',-15,0.1,15,1e-3) + self.plot_transfer = plot_config(1) + transfer_box = widgets.VBox( + [ + self.integration_transfer, + self.transfer_gates, + self.Vds_transfer.grid, + self.Vtg_transfer.grid, + self.Vbg_transfer.grid, + self.plot_transfer.grid + ] + ) + + + #output + self.integration_output = integration_time("SHORt") + self.output_gates = gate_selection("VTG","VBG","BOTH") + + self.Vds_output = primary('VDS',0,0.01,5,1e-2) + self.Vtg_output = secondary('VTG',-5,2,5,1e-3) + self.Vbg_output = secondary('VBG',-15,5,15,1e-3) + self.plot_output = plot_config(2) + + output_box = widgets.VBox( + [ + self.integration_output,self.output_gates, + self.Vds_output.grid, + self.Vtg_output.grid, + self.Vbg_output.grid, + self.plot_output.grid + ] + ) + + self.integration_gatediode = integration_time("MEDium") + self.gatediode_gates = gate_selection("VTG","VBG") + + self.Vg_gatediode=primary('VG',-5,0.05,5,1e-3) + self.plot_gatediode = plot_config(3) + gatediode_box = widgets.VBox( + [ + self.integration_gatediode, + self.gatediode_gates, + self.Vg_gatediode.grid, + self.plot_gatediode.grid + ] + ) + + #the tab widget + children = [transfer_box,output_box,gatediode_box] + titles = ["Transfer","Output","Gatediode"] + tab = widgets.Tab() + tab.children = children + tab.titles = titles + + display(tab) + + # Now the buttons + self.start = widgets.Button(description ='Start Measurement') + self.output = widgets.Output() + + self.export_ini = widgets.Button(description = 'Export as ini') + self.import_ini = widgets.Button(description ='Import from ini') + + # capture all widgets + self.all_widgets=[ + self.transfer_gates, + self.output_gates, + self.start, + self.transfer_check, + self.integration_transfer, + self.output_check, + self.integration_output, + self.gatediode_check, + self.integration_gatediode, + self.gatediode_gates, + self.export_ini, + self.import_ini + ] + add_widgets_to_list(self.sample,self.all_widgets) + add_widgets_to_list(self.Vds_transfer,self.all_widgets) + add_widgets_to_list(self.Vtg_transfer,self.all_widgets) + add_widgets_to_list(self.Vbg_transfer,self.all_widgets) + add_widgets_to_list(self.Vds_output,self.all_widgets) + add_widgets_to_list(self.Vtg_output,self.all_widgets) + add_widgets_to_list(self.Vbg_output,self.all_widgets) + add_widgets_to_list(self.Vg_gatediode,self.all_widgets) + add_widgets_to_list(self.plot_transfer,self.all_widgets) + add_widgets_to_list(self.plot_output,self.all_widgets) + add_widgets_to_list(self.plot_gatediode,self.all_widgets) + + line = widgets.HBox([self.start,self.import_ini,self.export_ini]) + display(line,self.output) + + \ No newline at end of file diff --git a/hp4155/ADU_for_double_gate_devices_version_3/lib/output.py b/hp4155/ADU_for_double_gate_devices_version_3/lib/output.py new file mode 100644 index 0000000000000000000000000000000000000000..dbc7fd30d66cfef0568f6fd9e3346d7b413d12d6 --- /dev/null +++ b/hp4155/ADU_for_double_gate_devices_version_3/lib/output.py @@ -0,0 +1,378 @@ +# This file contains the output measurements + +import sys +sys.path.insert(0, '..') #append parent directory + +import hp4155a +from help import * +from decimal import Decimal + +import os + +def VTG(ui,device): + device.del_user_functions() # delete all user functions + device.clear_error_stack() # clear error stack + # setup the smus + + norm = normalization_factor(ui.sample.width.value) + points = number_of_points(ui.Vtg_output) + + + smu_source = device.smu_dict() + smu_source.update(vname ='VS',iname = 'IS',mode = 'COMM',func='CONS') + + smu_top_gate = device.smu_dict() + smu_top_gate.update(vname = 'VTG',iname='ITG',mode = 'V',func='VAR2') + + smu_back_gate = device.smu_dict() + smu_back_gate.update(vname = 'VBG',iname='IBG',mode = 'COMM',func = 'CONS') + + smu_drain = device.smu_dict() + smu_drain.update(vname='VDS',iname='ID',mode = 'V',func = 'VAR1') + + # setup VAR1 + var1 = device.var1_dict() + var1.update( + mode = ui.Vds_output.hyst.value, + start = ui.Vds_output.start.value, + stop = ui.Vds_output.stop.value, + step = ui.Vds_output.step.value, + comp = ui.Vds_output.comp.value, + pcomp = ui.Vds_output.pcomp.value + ) + + var2=device.var2_dict() #Vds_output is always used in tranfer curve with the same config + var2.update( + start=ui.Vtg_output.start.value, + step=ui.Vtg_output.step.value, + points=points, + comp=ui.Vtg_output.comp.value, + pcomp=ui.Vtg_output.pcomp.value, + ) + + try: + device.setup_smu(ui.sample.top_gate.value,smu_top_gate) + device.setup_smu(ui.sample.drain.value,smu_drain) + device.setup_smu(ui.sample.back_gate.value,smu_back_gate) + device.setup_smu(ui.sample.source.value,smu_source) + + device.setup_var1(var1) + device.setup_var2(var2) + + device.integration_time(ui.integration_output.value) + variables_list = ['VDS','ID','VTG','ITG'] + device.variables_to_save(variables_list) + + plotted_variables = graph_tool(ui.plot_output,ui.sample.width.value,device) + + device.error_occured() + + device.single_measurement() + + while device.operation_completed()==False: + pass + + device.error_occured() + + if ui.sample.quick.value == False: + device.autoscaling() + + + values = dict([(variable,device.return_values(variable)) for variable in variables_list]) + df = get_dataframe_from_results(values) + except Exception as e: + error_box(e) + return + + # Append the normalized current + df["IDmm/uA/um"]= (df["ID/A"].apply(lambda x: Decimal(str(x))*Decimal(str(norm)))).astype('float') + df["ITGmm/uA/um"]= (df["ITG/A"].apply(lambda x: Decimal(str(x))*Decimal(str(norm)))).astype('float') + + default_filename = f"{ui.sample.sample.value}_{ui.sample.field.value}_{ui.sample.device.value}_TOP_GATE_A.txt" + file = create_file(default_filename) + + with open(file,'w') as f: + date = str(datetime.today().replace(microsecond=0)) + f.write(f"Output Curve at {date}"+"\n") + write_sample_information(ui.sample,f) + f.write("Sweeping Gate:VTG"+"\n\n") + f.write('Parameters\n') + f.write(f"VTG from {ui.Vtg_output.start.value}V to {ui.Vtg_output.stop.value}V with step {ui.Vtg_output.step.value}V"+"\n") + f.write(f"VDS from {ui.Vds_output.start.value}V to {ui.Vds_output.stop.value}V with step {ui.Vds_output.step.value}V"+"\n") + + if ui.Vtg_output.pcomp.value==0: + f.write(f"Top Gate Current Compliance/A:{ui.Vtg_output.comp.value}"+"\n") + else: + f.write(f"Top Gate Power Compliance/A:{ui.Vtg_output.pcomp.value}"+"\n") + + if ui.Vds_output.pcomp.value == 0: + f.write(f"Drain Current Compliance/A:{ui.Vds_output.comp.value}"+"\n") + else: + f.write(f"Drain Power Compliance/A:{ui.Vds_output.pcomp.value}"+"\n") + f.write(f"Integration Time:{ui.integration_output.value}"+"\n") + f.write("\nResults\n") + + df.to_csv(file,sep=" ",mode='a') + + if ui.sample.quick.value == False: + df["label"] = df.apply(lambda row:f"VTG = {row['VTG/V']} (V)",axis = 1) # assign labels + create_plot(ui.plot_output,df,file,"Top Gate Output Measurement", "Swept VTG voltages:",ui.sample.save_fig.value) + + +def VBG(ui,device): + device.del_user_functions() # delete all user functions + device.clear_error_stack() # clear error stack + # setup the smus + + norm = normalization_factor(ui.sample.width.value) + points = number_of_points(ui.Vbg_output) + + + smu_source = device.smu_dict() + smu_source.update(vname ='VS',iname = 'IS',mode = 'COMM',func='CONS') + + smu_top_gate = device.smu_dict() + smu_top_gate.update(vname = 'VTG',iname='ITG',mode = 'COMM',func='CONS') + + smu_back_gate = device.smu_dict() + smu_back_gate.update(vname = 'VBG',iname='IBG',mode = 'V',func = 'VAR2') + + smu_drain = device.smu_dict() + smu_drain.update(vname='VDS',iname='ID',mode = 'V',func = 'VAR1') + + # setup VAR1 + var1 = device.var1_dict() + var1.update( + mode = ui.Vds_output.hyst.value, + start = ui.Vds_output.start.value, + stop = ui.Vds_output.stop.value, + step = ui.Vds_output.step.value, + comp = ui.Vds_output.comp.value, + pcomp = ui.Vds_output.pcomp.value + ) + + var2=device.var2_dict() #Vds_output is always used in tranfer curve with the same config + var2.update( + start=ui.Vbg_output.start.value, + step=ui.Vbg_output.step.value, + points=points, + comp=ui.Vbg_output.comp.value, + pcomp=ui.Vbg_output.pcomp.value, + ) + + try: + device.setup_smu(ui.sample.top_gate.value,smu_top_gate) + device.setup_smu(ui.sample.drain.value,smu_drain) + device.setup_smu(ui.sample.back_gate.value,smu_back_gate) + device.setup_smu(ui.sample.source.value,smu_source) + + device.setup_var1(var1) + device.setup_var2(var2) + + device.integration_time(ui.integration_output.value) + + variables_list = ['VDS','ID','VBG','IBG'] + device.variables_to_save(variables_list) + + plotted_variables = graph_tool(ui.plot_output,ui.sample.width.value,device) + + device.error_occured() + + device.single_measurement() + + while device.operation_completed()==False: + pass + + device.error_occured() + + if ui.sample.quick.value == False: + device.autoscaling() + + + values = dict([(variable,device.return_values(variable)) for variable in variables_list]) + df = get_dataframe_from_results(values) + except Exception as e: + error_box(e) + return + + # Append the normalized current + df["IDmm/uA/um"]= (df["ID/A"].apply(lambda x: Decimal(str(x))*Decimal(str(norm)))).astype('float') + df["IBGmm/uA/um"]= (df["IBG/A"].apply(lambda x: Decimal(str(x))*Decimal(str(norm)))).astype('float') + + + # Save the results + default_filename = f"{ui.sample.sample.value}_{ui.sample.field.value}_{ui.sample.device.value}_BACK_GATE_A.txt" + file = create_file(default_filename) + + with open(file,'w') as f: + date = str(datetime.today().replace(microsecond=0)) + f.write(f"Output Curve at {date}"+"\n") + write_sample_information(ui.sample,f) + f.write("Sweeping Gate:VBG"+"\n\n") + f.write('Parameters\n') + f.write(f"VBG from {ui.Vbg_output.start.value}V to {ui.Vbg_output.stop.value}V with step {ui.Vbg_output.step.value}V"+"\n") + f.write(f"VDS from {ui.Vds_output.start.value}V to {ui.Vds_output.stop.value}V with step {ui.Vds_output.step.value}V"+"\n") + + if ui.Vbg_output.pcomp.value==0: + f.write(f"Back Gate Current Compliance/A:{ui.Vbg_output.comp.value}"+"\n") + else: + f.write(f"Back Gate Power Compliance/A:{ui.Vbg_output.pcomp.value}"+"\n") + + if ui.Vds_output.pcomp.value == 0: + f.write(f"Drain Current Compliance/A:{ui.Vds_output.comp.value}"+"\n") + else: + f.write(f"Drain Power Compliance/A:{ui.Vds_output.pcomp.value}"+"\n") + f.write(f"Integration Time:{ui.integration_output.value}"+"\n") + f.write("\nResults\n") + + df.to_csv(file,sep=" ",mode='a') + + if ui.sample.quick.value == False: + df["label"] = df.apply(lambda row:f"VBG = {row['VBG/V']} (V)",axis = 1) # assign labels + create_plot(ui.plot_output,df,file,"Back Gate Output Measurement", "Swept VBG voltages:",ui.sample.save_fig.value) + +def SEQ(ui,device): + device.del_user_functions() # delete all user functions + device.clear_error_stack() # clear error stack + norm = normalization_factor(ui.sample.width.value) + points_VTG = number_of_points(ui.Vtg_output) + points_VBG = number_of_points(ui.Vbg_output) + + try: + values_VBG = np.linspace(ui.Vbg_output.start.value, ui.Vbg_output.stop.value,num = points_VBG,endpoint = True) + except: + error_box("Invalid VBG values!") + return + + smu_source = device.smu_dict() + smu_source.update(vname ='VS',iname = 'IS',mode = 'COMM',func='CONS') + + smu_top_gate = device.smu_dict() + smu_top_gate.update(vname = 'VTG',iname='ITG',mode = 'V',func='VAR2') + + smu_back_gate = device.smu_dict() + smu_back_gate.update(vname = 'VBG',iname='IBG',mode = 'V',func = 'CONS') + + smu_drain = device.smu_dict() + smu_drain.update(vname='VDS',iname='ID',mode = 'V',func = 'VAR1') + + # setup VAR1 + var1 = device.var1_dict() + var1.update( + mode = ui.Vds_output.hyst.value, + start = ui.Vds_output.start.value, + stop = ui.Vds_output.stop.value, + step = ui.Vds_output.step.value, + comp = ui.Vds_output.comp.value, + pcomp = ui.Vds_output.pcomp.value + ) + + var2=device.var2_dict() #Vds_output is always used in tranfer curve with the same config + var2.update( + start=ui.Vtg_output.start.value, + step=ui.Vtg_output.step.value, + points=points_VTG, + comp=ui.Vtg_output.comp.value, + pcomp=ui.Vtg_output.pcomp.value, + ) + + try: + device.setup_smu(ui.sample.top_gate.value,smu_top_gate) + device.setup_smu(ui.sample.drain.value,smu_drain) + device.setup_smu(ui.sample.back_gate.value,smu_back_gate) + device.setup_smu(ui.sample.source.value,smu_source) + + device.setup_var1(var1) + device.setup_var2(var2) + + device.integration_time(ui.integration_output.value) + + variables_list =["VBG","IBG","VDS","ID","VTG","ITG"] + device.variables_to_save(variables_list) + + plotted_variables = graph_tool(ui.plot_output,ui.sample.width.value,device) + + device.error_occured() + for i,value in enumerate(values_VBG): + cons = device.cons_smu_dict() + cons.update(comp = ui.Vbg_output.comp.value, value = value) + device.setup_cons_smu(ui.sample.back_gate.value,cons) + + if i == 0: + device.single_measurement() + else: + device.append_measurement() + + while device.operation_completed()==False: + pass + device.error_occured() + + if ui.sample.quick.value == False: + device.autoscaling() + + values = dict([(variable,device.return_values(variable)) for variable in variables_list]) + df = get_dataframe_from_results(values) + + except Exception as e: + error_box(e) + return + + # Append the normalized current + df["IDmm/uA/um"]= (df["ID/A"].apply(lambda x: Decimal(str(x))*Decimal(str(norm)))).astype('float') + df["IBGmm/uA/um"]= (df["IBG/A"].apply(lambda x: Decimal(str(x))*Decimal(str(norm)))).astype('float') + df["ITGmm/uA/um"]= (df["ITG/A"].apply(lambda x: Decimal(str(x))*Decimal(str(norm)))).astype('float') + + + # Save the results + default_filename = f"{ui.sample.sample.value}_{ui.sample.field.value}_{ui.sample.device.value}_BOTH_GATES_SEQ_A.txt" + file = create_file(default_filename) + + with open(file,'w') as f: + date = str(datetime.today().replace(microsecond=0)) + f.write(f"output Curve at {date}"+"\n") + write_sample_information(ui.sample,f) + f.write("Sweeping Gate:VTG,VBG sequentially"+"\n\n") + f.write('Parameters\n') + f.write(f"VBG from {ui.Vbg_output.start.value}V to {ui.Vbg_output.stop.value}V with step {ui.Vbg_output.step.value}V"+"\n") + f.write(f"VTG from {ui.Vtg_output.start.value}V to {ui.Vtg_output.stop.value}V with step {ui.Vbg_output.step.value}V"+"\n") + f.write(f"VDS from {ui.Vds_output.start.value}V to {ui.Vds_output.stop.value}V with step {ui.Vds_output.step.value}V"+"\n") + + f.write(f"Back Gate Current Compliance/A:{ui.Vbg_output.comp.value}"+"\n") + + if ui.Vtg_output.pcomp.value==0: + f.write(f"Top Gate Current Compliance/A:{ui.Vtg_output.comp.value}"+"\n") + else: + f.write(f"Top Gate Power Compliance/A:{ui.Vtg_output.pcomp.value}"+"\n") + + if ui.Vds_output.pcomp.value == 0: + f.write(f"Drain Current Compliance/A:{ui.Vds_output.comp.value}"+"\n") + else: + f.write(f"Drain Power Compliance/A:{ui.Vds_output.pcomp.value}"+"\n") + f.write(f"Integration Time:{ui.integration_output.value}"+"\n") + f.write("\nResults\n") + + df.to_csv(file,sep=" ",mode='a') + + if ui.sample.quick.value == False: + df["label"] = df.apply(lambda row:f"VTG = {row['VTG/V']} (V), VBG = {row['VBG/V']} (V)",axis = 1) # assign labels + create_plot(ui.plot_output,df,file,"Sequential Sweep of Both Gates Output Measurement", "Swept voltages:",ui.sample.save_fig.value) + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hp4155/ADU_for_double_gate_devices_version_3/lib/transfer.py b/hp4155/ADU_for_double_gate_devices_version_3/lib/transfer.py new file mode 100644 index 0000000000000000000000000000000000000000..d9851d71b5fbe4f447bd2fcbc27465118cc008fe --- /dev/null +++ b/hp4155/ADU_for_double_gate_devices_version_3/lib/transfer.py @@ -0,0 +1,500 @@ +# This file contains the transfer measurements + +import sys +sys.path.insert(0, '..') #append parent directory + +import hp4155a +from help import * +from decimal import Decimal + +import os + +def VTG(ui,device): + device.del_user_functions() # delete all user functions + device.clear_error_stack() # clear error stack + # setup the smus + + norm = normalization_factor(ui.sample.width.value) + points = number_of_points(ui.Vds_transfer) + + smu_source = device.smu_dict() + smu_source.update(vname ='VS',iname = 'IS',mode = 'COMM',func='CONS') + + smu_top_gate = device.smu_dict() + smu_top_gate.update(vname = 'VTG',iname='ITG',mode = 'V',func='VAR1') + + smu_back_gate = device.smu_dict() + smu_back_gate.update(vname = 'VBG',iname='IBG',mode = 'COMM',func = 'CONS') + + smu_drain = device.smu_dict() + smu_drain.update(vname='VDS',iname='ID',mode = 'V',func = 'VAR2') + + # setup VAR1 + var1 = device.var1_dict() + var1.update( + mode = ui.Vtg_transfer.hyst.value, + start = ui.Vtg_transfer.start.value, + stop = ui.Vtg_transfer.stop.value, + step = ui.Vtg_transfer.step.value, + comp = ui.Vtg_transfer.comp.value, + pcomp = ui.Vtg_transfer.pcomp.value + ) + + var2=device.var2_dict() #Vds_output is always used in tranfer curve with the same config + var2.update( + start=ui.Vds_transfer.start.value, + step=ui.Vds_transfer.step.value, + points=points, + comp=ui.Vds_transfer.comp.value, + pcomp=ui.Vds_transfer.pcomp.value, + ) + + try: + device.setup_smu(ui.sample.top_gate.value,smu_top_gate) + device.setup_smu(ui.sample.drain.value,smu_drain) + device.setup_smu(ui.sample.back_gate.value,smu_back_gate) + device.setup_smu(ui.sample.source.value,smu_source) + + device.setup_var1(var1) + device.setup_var2(var2) + + device.integration_time(ui.integration_transfer.value) + + variables_list =["VTG","ITG","VDS","ID"] + device.variables_to_save(variables_list) + + plotted_variables = graph_tool(ui.plot_transfer,ui.sample.width.value,device) + + device.error_occured() + + device.single_measurement() + while device.operation_completed()==False: + pass + + device.error_occured() + + if ui.sample.quick.value == False: + device.autoscaling() + + + values = dict([(variable,device.return_values(variable)) for variable in variables_list]) + df = get_dataframe_from_results(values) + except Exception as e: + error_box(e) + return + + # Append the normalized current + df["IDmm/uA/um"]= (df["ID/A"].apply(lambda x: Decimal(str(x))*Decimal(str(norm)))).astype('float') + df["ITGmm/uA/um"]= (df["ITG/A"].apply(lambda x: Decimal(str(x))*Decimal(str(norm)))).astype('float') + + # Save the results + default_filename = f"{ui.sample.sample.value}_{ui.sample.field.value}_{ui.sample.device.value}_TOP_GATE_U.txt" + file = create_file(default_filename) + + with open(file,'w') as f: + date = str(datetime.today().replace(microsecond=0)) + f.write(f"Transfer Curve at {date}"+"\n") + write_sample_information(ui.sample,f) + f.write("Sweeping Gate:VTG"+"\n\n") + f.write('Parameters\n') + f.write(f"VTG from {ui.Vtg_transfer.start.value}V to {ui.Vtg_transfer.stop.value}V with step {ui.Vtg_transfer.step.value}V"+"\n") + f.write(f"VDS from {ui.Vds_transfer.start.value}V to {ui.Vds_transfer.stop.value}V with step {ui.Vds_transfer.step.value}V"+"\n") + + if ui.Vtg_transfer.pcomp.value==0: + f.write(f"Top Gate Current Compliance/A:{ui.Vtg_transfer.comp.value}"+"\n") + else: + f.write(f"Top Gate Power Compliance/A:{ui.Vtg_transfer.pcomp.value}"+"\n") + + if ui.Vds_transfer.pcomp.value == 0: + f.write(f"Drain Current Compliance/A:{ui.Vds_transfer.comp.value}"+"\n") + else: + f.write(f"Drain Power Compliance/A:{ui.Vds_transfer.pcomp.value}"+"\n") + f.write(f"Integration Time:{ui.integration_transfer.value}"+"\n") + f.write("\nResults\n") + + df.to_csv(file,sep=" ",mode='a') + + if ui.sample.quick.value == False: + df["label"] = df.apply(lambda row:f"VDS = {row['VDS/V']} (V)",axis = 1) # assign labels + create_plot(ui.plot_transfer,df,file,"Top Gate Transfer Measurement", "Swept VDS voltages:",ui.sample.save_fig.value) + +def VBG(ui,device): + device.del_user_functions() # delete all user functions + device.clear_error_stack() # clear error stack + # setup the smus + + norm = normalization_factor(ui.sample.width.value) + points = number_of_points(ui.Vds_transfer) + + smu_source = device.smu_dict() + smu_source.update(vname ='VS',iname = 'IS',mode = 'COMM',func='CONS') + + smu_top_gate = device.smu_dict() + smu_top_gate.update(vname = 'VTG',iname='ITG',mode = 'COMM',func='CONS') + + smu_back_gate = device.smu_dict() + smu_back_gate.update(vname = 'VBG',iname='IBG',mode = 'V',func = 'VAR1') + + smu_drain = device.smu_dict() + smu_drain.update(vname='VDS',iname='ID',mode = 'V',func = 'VAR2') + + + var1 = device.var1_dict() + var1.update( + mode = ui.Vbg_transfer.hyst.value, + start = ui.Vbg_transfer.start.value, + stop = ui.Vbg_transfer.stop.value, + step = ui.Vbg_transfer.step.value, + comp = ui.Vbg_transfer.comp.value, + pcomp = ui.Vbg_transfer.pcomp.value + ) + + var2=device.var2_dict() #Vds_output is always used in tranfer curve with the same config + var2.update( + start=ui.Vds_transfer.start.value, + step=ui.Vds_transfer.step.value, + points=points, + comp=ui.Vds_transfer.comp.value, + pcomp=ui.Vds_transfer.pcomp.value, + ) + + try: + device.setup_smu(ui.sample.top_gate.value,smu_top_gate) + device.setup_smu(ui.sample.drain.value,smu_drain) + device.setup_smu(ui.sample.back_gate.value,smu_back_gate) + device.setup_smu(ui.sample.source.value,smu_source) + + device.setup_var1(var1) + device.setup_var2(var2) + + device.integration_time(ui.integration_transfer.value) + + variables_list =["VBG","IBG","VDS","ID"] + device.variables_to_save(variables_list) + + plotted_variables = graph_tool(ui.plot_transfer,ui.sample.width.value,device) + + device.error_occured() + + device.single_measurement() + + while device.operation_completed()==False: + pass + + device.error_occured() + + if ui.sample.quick.value == False: + device.autoscaling() + + + values = dict([(variable,device.return_values(variable)) for variable in variables_list]) + df = get_dataframe_from_results(values) + except Exception as e: + error_box(e) + return + + # Append the normalized current + df["IDmm/uA/um"]= (df["ID/A"].apply(lambda x: Decimal(str(x))*Decimal(str(norm)))).astype('float') + df["IBGmm/uA/um"]= (df["IBG/A"].apply(lambda x: Decimal(str(x))*Decimal(str(norm)))).astype('float') + + + # Save the results + default_filename = f"{ui.sample.sample.value}_{ui.sample.field.value}_{ui.sample.device.value}_BACK_GATE_U.txt" + file = create_file(default_filename) + + with open(file,'w') as f: + date = str(datetime.today().replace(microsecond=0)) + f.write(f"Transfer Curve at {date}"+"\n") + write_sample_information(ui.sample,f) + f.write("Sweeping Gate:VBG"+"\n\n") + f.write('Parameters\n') + f.write(f"VBG from {ui.Vbg_transfer.start.value}V to {ui.Vbg_transfer.stop.value}V with step {ui.Vbg_transfer.step.value}V"+"\n") + f.write(f"VDS from {ui.Vds_transfer.start.value}V to {ui.Vds_transfer.stop.value}V with step {ui.Vds_transfer.step.value}V"+"\n") + + if ui.Vbg_transfer.pcomp.value==0: + f.write(f"Back Gate Current Compliance/A:{ui.Vbg_transfer.comp.value}"+"\n") + else: + f.write(f"Back Gate Power Compliance/A:{ui.Vbg_transfer.pcomp.value}"+"\n") + + if ui.Vds_transfer.pcomp.value == 0: + f.write(f"Drain Current Compliance/A:{ui.Vds_transfer.comp.value}"+"\n") + else: + f.write(f"Drain Power Compliance/A:{ui.Vds_transfer.pcomp.value}"+"\n") + f.write(f"Integration Time:{ui.integration_transfer.value}"+"\n") + f.write("\nResults\n") + + df.to_csv(file,sep=" ",mode='a') + + if ui.sample.quick.value == False: + df["label"] = df.apply(lambda row:f"VDS = {row['VDS/V']} (V)",axis = 1) # assign labels + create_plot(ui.plot_transfer,df,file,"Back Gate Transfer Measurement", "Swept VDS voltages:",ui.sample.save_fig.value) + + +# Simultaneously bóth gates +def SIM(ui,device): + device.del_user_functions() # delete all user functions + device.clear_error_stack() # clear error stack + # setup the smus + + norm = normalization_factor(ui.sample.width.value) + points = number_of_points(ui.Vds_transfer) + + smu_source = device.smu_dict() + smu_source.update(vname ='VS',iname = 'IS',mode = 'COMM',func='CONS') + + smu_top_gate = device.smu_dict() + smu_top_gate.update(vname = 'VTG',iname='ITG',mode = 'V',func='VAR1') + + smu_back_gate = device.smu_dict() + smu_back_gate.update(vname = 'VBG',iname='IBG',mode = 'V',func = 'VARD') + + smu_drain = device.smu_dict() + smu_drain.update(vname='VDS',iname='ID',mode = 'V',func = 'VAR2') + + + var1 = device.var1_dict() + var1.update( + mode = ui.Vtg_transfer.hyst.value, + start = ui.Vtg_transfer.start.value, + stop = ui.Vtg_transfer.stop.value, + step = ui.Vtg_transfer.step.value, + comp = ui.Vtg_transfer.comp.value, + pcomp = ui.Vtg_transfer.pcomp.value + ) + + var2=device.var2_dict() #Vds_output is always used in tranfer curve with the same config + var2.update( + start=ui.Vds_transfer.start.value, + step=ui.Vds_transfer.step.value, + points=points, + comp=ui.Vds_transfer.comp.value, + pcomp=ui.Vds_transfer.pcomp.value, + ) + + #calculate parameters for VARD + ratio,offset = calculate_line(ui.Vtg_transfer,ui.Vbg_transfer) + + # update VBG step + ui.Vbg_transfer.step.value = Decimal(str(ratio)) * Decimal(str(ui.Vtg_transfer.step.value)) + + vard = device.vard_dict() + vard.update( + offset = offset, + ratio = ratio, + comp = ui.Vbg_transfer.comp.value, + pcomp = ui.Vbg_transfer.pcomp.value + ) + + try: + device.setup_smu(ui.sample.top_gate.value,smu_top_gate) + device.setup_smu(ui.sample.drain.value,smu_drain) + device.setup_smu(ui.sample.back_gate.value,smu_back_gate) + device.setup_smu(ui.sample.source.value,smu_source) + + device.setup_var1(var1) + device.setup_var2(var2) + device.setup_vard(vard) + + device.integration_time(ui.integration_transfer.value) + + variables_list =["VBG","IBG","VDS","ID","VTG","ITG"] + device.variables_to_save(variables_list) + + plotted_variables = graph_tool(ui.plot_transfer,ui.sample.width.value,device) + + device.error_occured() + + device.single_measurement() + + while device.operation_completed()==False: + pass + + device.error_occured() + + if ui.sample.quick.value == False: + device.autoscaling() + + + values = dict([(variable,device.return_values(variable)) for variable in variables_list]) + df = get_dataframe_from_results(values) + except Exception as e: + error_box(e) + return + + # Append the normalized current + df["IDmm/uA/um"]= (df["ID/A"].apply(lambda x: Decimal(str(x))*Decimal(str(norm)))).astype('float') + df["IBGmm/uA/um"]= (df["IBG/A"].apply(lambda x: Decimal(str(x))*Decimal(str(norm)))).astype('float') + df["ITGmm/uA/um"]= (df["ITG/A"].apply(lambda x: Decimal(str(x))*Decimal(str(norm)))).astype('float') + + + # Save the results + default_filename = f"{ui.sample.sample.value}_{ui.sample.field.value}_{ui.sample.device.value}_BOTH_GATES_SIM_U.txt" + file = create_file(default_filename) + + with open(file,'w') as f: + date = str(datetime.today().replace(microsecond=0)) + f.write(f"Transfer Curve at {date}"+"\n") + write_sample_information(ui.sample,f) + f.write("Sweeping Gate:VTG,VBG simultaneously"+"\n\n") + f.write('Parameters\n') + f.write(f"VBG from {ui.Vbg_transfer.start.value}V to {ui.Vbg_transfer.stop.value}V with step {ui.Vbg_transfer.step.value}V"+"\n") + f.write(f"VTG from {ui.Vtg_transfer.start.value}V to {ui.Vtg_transfer.stop.value}V with step {ui.Vbg_transfer.step.value}V"+"\n") + f.write(f"VDS from {ui.Vds_transfer.start.value}V to {ui.Vds_transfer.stop.value}V with step {ui.Vds_transfer.step.value}V"+"\n") + + if ui.Vbg_transfer.pcomp.value==0: + f.write(f"Back Gate Current Compliance/A:{ui.Vbg_transfer.comp.value}"+"\n") + else: + f.write(f"Back Gate Power Compliance/A:{ui.Vbg_transfer.pcomp.value}"+"\n") + + if ui.Vtg_transfer.pcomp.value==0: + f.write(f"Top Gate Current Compliance/A:{ui.Vtg_transfer.comp.value}"+"\n") + else: + f.write(f"Top Gate Power Compliance/A:{ui.Vtg_transfer.pcomp.value}"+"\n") + + if ui.Vds_transfer.pcomp.value == 0: + f.write(f"Drain Current Compliance/A:{ui.Vds_transfer.comp.value}"+"\n") + else: + f.write(f"Drain Power Compliance/A:{ui.Vds_transfer.pcomp.value}"+"\n") + f.write(f"Integration Time:{ui.integration_transfer.value}"+"\n") + f.write("\nResults\n") + + df.to_csv(file,sep=" ",mode='a') + + if ui.sample.quick.value == False: + df["label"] = df.apply(lambda row:f"VDS = {row['VDS/V']} (V)",axis = 1) # assign labels + create_plot(ui.plot_transfer,df,file,"Simultaneous Sweep of Both Gates Transfer Measurement", "Swept VDS voltages:",ui.sample.save_fig.value) + +def SEQ(ui,device): + device.del_user_functions() # delete all user functions + device.clear_error_stack() # clear error stack + norm = normalization_factor(ui.sample.width.value) + points_VDS = number_of_points(ui.Vds_transfer) + points_VBG = number_of_points(ui.Vbg_transfer) + + + try: + values_VBG = np.linspace(ui.Vbg_transfer.start.value,ui.Vbg_transfer.stop.value,num = points_VBG, endpoint = True) + except: + error_box("Invalid VBG values!") + return + + smu_source = device.smu_dict() + smu_source.update(vname ='VS',iname = 'IS',mode = 'COMM',func='CONS') + + smu_top_gate = device.smu_dict() + smu_top_gate.update(vname = 'VTG',iname='ITG',mode = 'V',func='VAR1') + + smu_back_gate = device.smu_dict() + smu_back_gate.update(vname = 'VBG',iname='IBG',mode = 'V',func = 'CONS') # Only VTG is swept + + smu_drain = device.smu_dict() + smu_drain.update(vname='VDS',iname='ID',mode = 'V',func = 'VAR2') + + + # setup VAR1 + var1 = device.var1_dict() + var1.update( + mode = ui.Vtg_transfer.hyst.value, + start = ui.Vtg_transfer.start.value, + stop = ui.Vtg_transfer.stop.value, + step = ui.Vtg_transfer.step.value, + comp = ui.Vtg_transfer.comp.value, + pcomp = ui.Vtg_transfer.pcomp.value + ) + + var2=device.var2_dict() #Vds_output is always used in tranfer curve with the same config + var2.update( + start=ui.Vds_transfer.start.value, + step=ui.Vds_transfer.step.value, + points=points_VDS, + comp=ui.Vds_transfer.comp.value, + pcomp=ui.Vds_transfer.pcomp.value, + ) + + try: + device.setup_smu(ui.sample.top_gate.value,smu_top_gate) + device.setup_smu(ui.sample.drain.value,smu_drain) + device.setup_smu(ui.sample.back_gate.value,smu_back_gate) + device.setup_smu(ui.sample.source.value,smu_source) + + device.setup_var1(var1) # VTG is swept first + device.setup_var2(var2) # Then VDS is swept + + device.integration_time(ui.integration_transfer.value) + + variables_list =["VBG","IBG","VDS","ID","VTG","ITG"] + device.variables_to_save(variables_list) + + plotted_variables = graph_tool(ui.plot_transfer,ui.sample.width.value,device) + + device.error_occured() + + # In the end VBG is swept + for i,value in enumerate(values_VBG): + cons = device.cons_smu_dict() + cons.update(comp = ui.Vbg_transfer.comp.value, value = value) + device.setup_cons_smu(ui.sample.back_gate.value,cons) + + if i == 0: + device.single_measurement() + else: + device.append_measurement() + + while device.operation_completed()==False: + pass + device.error_occured() + + if ui.sample.quick.value == False: + device.autoscaling() + + values = dict([(variable,device.return_values(variable)) for variable in variables_list]) + df = get_dataframe_from_results(values) + + except Exception as e: + error_box(e) + return + + # Append the normalized current + df["IDmm/uA/um"]= (df["ID/A"].apply(lambda x: Decimal(str(x))*Decimal(str(norm)))).astype('float') + df["IBGmm/uA/um"]= (df["IBG/A"].apply(lambda x: Decimal(str(x))*Decimal(str(norm)))).astype('float') + df["ITGmm/uA/um"]= (df["ITG/A"].apply(lambda x: Decimal(str(x))*Decimal(str(norm)))).astype('float') + + + # Save the results + default_filename = f"{ui.sample.sample.value}_{ui.sample.field.value}_{ui.sample.device.value}_BOTH_GATES_SEQ_U.txt" + file = create_file(default_filename) + + with open(file,'w') as f: + date = str(datetime.today().replace(microsecond=0)) + f.write(f"Transfer Curve at {date}"+"\n") + write_sample_information(ui.sample,f) + f.write("Sweeping Gate:VTG,VBG sequentially"+"\n\n") + f.write('Parameters\n') + f.write(f"VBG from {ui.Vbg_transfer.start.value}V to {ui.Vbg_transfer.stop.value}V with step {ui.Vbg_transfer.step.value}V"+"\n") + f.write(f"VTG from {ui.Vtg_transfer.start.value}V to {ui.Vtg_transfer.stop.value}V with step {ui.Vbg_transfer.step.value}V"+"\n") + f.write(f"VDS from {ui.Vds_transfer.start.value}V to {ui.Vds_transfer.stop.value}V with step {ui.Vds_transfer.step.value}V"+"\n") + + f.write(f"Back Gate Current Compliance/A:{ui.Vbg_transfer.comp.value}"+"\n") + + if ui.Vtg_transfer.pcomp.value==0: + f.write(f"Top Gate Current Compliance/A:{ui.Vtg_transfer.comp.value}"+"\n") + else: + f.write(f"Top Gate Power Compliance/A:{ui.Vtg_transfer.pcomp.value}"+"\n") + + if ui.Vds_transfer.pcomp.value == 0: + f.write(f"Drain Current Compliance/A:{ui.Vds_transfer.comp.value}"+"\n") + else: + f.write(f"Drain Power Compliance/A:{ui.Vds_transfer.pcomp.value}"+"\n") + f.write(f"Integration Time:{ui.integration_transfer.value}"+"\n") + f.write("\nResults\n") + + df.to_csv(file,sep=" ",mode='a') + + if ui.sample.quick.value == False: + if ui.plot_transfer.x.value == 'VTG': + df["label"] = df.apply(lambda row:f"VDS = {row['VDS/V']} (V), VBG = {row['VBG/V']} (V)",axis = 1) # assign labels + + else: + df["label"] = df.apply(lambda row:f"VDS = {row['VDS/V']} (V), VTG = {row['VTG/V']} (V)",axis = 1) # assign labels + + create_plot(ui.plot_transfer,df,file,"Sequential Sweep of Both Gates Transfer Measurement", "Swept voltages:",ui.sample.save_fig.value) \ No newline at end of file