diff --git a/hp4155/ADU for double gate devices-test/double_gate_ADU.py b/hp4155/ADU for double gate devices-test/double_gate_ADU.py new file mode 100644 index 0000000000000000000000000000000000000000..4b66de872acefdc44a93bbdbfacf359d7c66e092 --- /dev/null +++ b/hp4155/ADU for double gate devices-test/double_gate_ADU.py @@ -0,0 +1,597 @@ +import sys +sys.path.insert(0, './lib') +sys.path.insert(0, '..') #append parent directory + + +from interface import * +from help import * +from measurements import * +import configparser + +# Create the grids +#create the information grid +style = {'description_width': 'initial'} +sample,smu_map = information_box_new() + + +###end of sampling information####################################### + +# move checkboxes outside of the tabs +transfer_check,integration_transfer,transfer_gates = header('Transfer Curve',"MEDium") +output_check,integration_output,output_gates = header('Output Curve',"SHORt") +gatediode_check,integration_gatediode,_=header('Gatediode',"MEDium") + +checkboxes = widgets.HBox([transfer_check,output_check,gatediode_check]) +display(checkboxes) + +#transfer +Vds_transfer_widgets,Vds_transfer = secondary('VDS',0.05,0.95,1,1e-2) +Vtg_transfer_widgets,Vtg_transfer = primary('VTG',-5,0.01,5,1e-3) +Vbg_transfer_widgets,Vbg_transfer = synchronous('VBG',-15,0.1,15,1e-3) +plot_transfer_widgets,plot_transfer = plot_config(1) +transfer_box = widgets.VBox([integration_transfer,transfer_gates,Vds_transfer_widgets,Vtg_transfer_widgets,Vbg_transfer_widgets,plot_transfer_widgets]) + + +#output +Vds_output_widgets,Vds_output = primary('VDS',0,0.01,5,1e-2) +Vtg_output_widgets,Vtg_output = secondary('VTG',-5,2,5,1e-3) +Vbg_output_widgets,Vbg_output = additional_secondary('VBG',-15,5,15,1e-3) +plot_output_widgets,plot_output = plot_config(2) + +output_box = widgets.VBox([integration_output,output_gates,Vds_output_widgets,Vtg_output_widgets,Vbg_output_widgets,plot_output_widgets]) +#GateDiodde +terminal = widgets.Dropdown( + options = ['VTG','VBG'], + description = 'Selected Gate:', + value ='VTG', + style= {'description_width': 'initial'} +) +Vg_gatediode_widgets,Vg_gatediode=primary('VG',-5,0.05,5,1e-3) +plot_gatediode_widgets,plot_gatediode = plot_config(3) +gatediode_box = widgets.VBox([integration_gatediode,terminal,Vg_gatediode_widgets,plot_gatediode_widgets]) + +#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) + +button = widgets.Button(description ='Start Measurement') +output = widgets.Output() + +export_ini_button = widgets.Button(description = 'Export as ini') +import_ini_button = widgets.Button(description='Import from ini') + +all_widgets =[transfer_gates,output_gates,button,transfer_check,integration_transfer,output_check,integration_output,gatediode_check,integration_gatediode,terminal,export_ini_button,import_ini_button] + +add_widgets_to_list(sample,all_widgets) +add_widgets_to_list(smu_map,all_widgets) +add_widgets_to_list(Vds_transfer,all_widgets) +add_widgets_to_list(Vtg_transfer,all_widgets) +add_widgets_to_list(Vbg_transfer,all_widgets) +add_widgets_to_list(Vds_output,all_widgets) +add_widgets_to_list(Vtg_output,all_widgets) +add_widgets_to_list(Vbg_output,all_widgets) +add_widgets_to_list(Vg_gatediode,all_widgets) + +line = widgets.HBox([button,import_ini_button,export_ini_button]) +display(line,output) +device = hp4155a.HP4155a('GPIB0::17::INSTR') + +def on_start_clicked(b): + with output: + clear_output() + #disable all widgets + disable_widgets(all_widgets) + + #additional code to check smu configuration fast without modifing a lot of code + try: + check_configuration(smu_map) + except Exception as e: + error_box(e) + enable_widgets(all_widgets) + return + + Setup(device) #setup the device + + #for all measurements the same (copy the interface with values for parameter setting) + map = smu_map.copy() + for key,value in map.items(): + map[key]= value.value + + sample_copy = sample.copy() + for key,value in sample_copy.items(): + sample_copy[key]=value.value + + smu_s = device.smu_dict() + smu_s.update(vname ='VS',iname = 'IS',mode = 'COMM',func='CONS') #Source smu is always grounded + + if transfer_check.value == True: + plot = plot_transfer.copy() + for key,value in plot.items(): + plot[key] = value.value + + points = number_of_points(Vds_transfer) + + var2=device.var2_dict() #Vds_output is always used in tranfer curve with the same config + var2.update( + start=Vds_transfer['start'].value, + step=Vds_transfer['step'].value, + points=points, + comp=Vds_transfer['comp'].value, + pcomp=Vds_transfer['pcomp'].value, + stop = Vds_transfer['stop'].value #not important for setting VAR2 only parameters in file + ) + + # also drain smu is the same + smu_d = device.smu_dict() + smu_d.update(vname='VDS',iname='ID',mode = 'V',func = 'VAR2') + match transfer_gates.value: + case 'VTG' if check_values(Vtg_transfer,'primary') and check_values(Vds_transfer,'secondary'): + smu_t = device.smu_dict() + smu_t.update(vname = 'VTG',iname='ITG',mode = 'V',func='VAR1') + smu_b = device.smu_dict() + smu_b.update(vname = 'VBG',iname='IBG',mode = 'COMM',func = 'CONS') + + var1 = device.var1_dict() + var1.update( + mode=Vtg_transfer['hyst'].value, + start=Vtg_transfer['start'].value, + stop=Vtg_transfer['stop'].value, + step=Vtg_transfer['step'].value, + comp =Vtg_transfer['comp'].value, + pcomp=Vtg_transfer['pcomp'].value + ) + #define the final dict + meas_dict = { + "SAMPLE":sample_copy, + "MAP" : map, + "SMU_T":smu_t, + "SMU_D": smu_d, + "SMU_B":smu_b, + "SMU_S":smu_s, + "VAR1":var1, + "VAR2":var2, + "PLOT": plot, # plot in the tool + "INTEGRATION": integration_transfer.value, + } + #measure (later) + Transfer_VTG(device,meas_dict) + + case 'VBG' if check_values(Vbg_transfer,'primary') and check_values(Vds_transfer,'secondary'): + smu_t = device.smu_dict() + smu_t.update(vname = 'VTG',iname='ITG',mode = 'COMM',func='CONS') + smu_b = device.smu_dict() + smu_b.update(vname = 'VBG',iname='IBG',mode = 'V',func = 'VAR1') + + var1 = device.var1_dict() + var1.update( + mode=Vbg_transfer['hyst'].value, + start=Vbg_transfer['start'].value, + stop=Vbg_transfer['stop'].value, + step=Vbg_transfer['step'].value, + comp =Vbg_transfer['comp'].value, + pcomp=Vbg_transfer['pcomp'].value + ) + + #define the final dict + meas_dict = { + "SAMPLE":sample_copy, + "MAP" : map, + "SMU_T":smu_t, + "SMU_D": smu_d, + "SMU_B":smu_b, + "SMU_S":smu_s, + "VAR1":var1, + "VAR2":var2, + "PLOT": plot, # plot in the tool + "INTEGRATION": integration_transfer.value, + } + #measure (later) + Transfer_VBG(device,meas_dict) + + + case 'BOTH' if check_values(Vbg_transfer,'synchronous') and check_values(Vds_transfer,'secondary') and check_values(Vtg_transfer,'primary'): + smu_t = device.smu_dict() + smu_t.update(vname = 'VTG',iname='ITG',mode = 'V',func='VAR1') + smu_b = device.smu_dict() + smu_b.update(vname = 'VBG',iname='IBG',mode = 'V',func = 'VARD') + + var1 = device.var1_dict() + var1.update( + mode=Vtg_transfer['hyst'].value, + start=Vtg_transfer['start'].value, + stop=Vtg_transfer['stop'].value, + step=Vtg_transfer['step'].value, + comp =Vtg_transfer['comp'].value, + pcomp=Vtg_transfer['pcomp'].value + ) + + #calculate parameters for VARD + ratio,offset = calculate_line(Vtg_transfer,Vbg_transfer) + + # update VBG step + Vbg_transfer["step"].value = Decimal(str(ratio)) * Decimal(str(Vtg_transfer["step"].value)) + + + vard = device.vard_dict() + vard.update( + offset = offset, + ratio = ratio, + comp = Vbg_transfer["comp"].value, + pcomp = Vbg_transfer["pcomp"].value, + start = Vbg_transfer["start"].value, + step = Vbg_transfer['step'].value, + stop = Vbg_transfer['stop'].value, + ) + + #define the final dict + meas_dict = { + "SAMPLE":sample_copy, + "MAP" : map, + "SMU_T":smu_t, + "SMU_D": smu_d, + "SMU_B":smu_b, + "SMU_S":smu_s, + "VAR1":var1, + "VAR2":var2, + "PLOT": plot, # plot in the tool + "INTEGRATION": integration_transfer.value, + "VARD":vard + } + + #measure (later) + Transfer_BOTH(device,meas_dict) + case _ : + information_box("Transfer Measurement skipped due to invalid parameters") + + if output_check.value == True: + smu_d = device.smu_dict() + smu_d.update(vname='VDS',iname='ID',mode = 'V',func = 'VAR1') + + var1 = device.var1_dict() + var1.update( + mode=Vds_output['hyst'].value, + start=Vds_output['start'].value, + stop=Vds_output['stop'].value, + step=Vds_output['step'].value, + comp =Vds_output['comp'].value, + pcomp=Vds_output['pcomp'].value + ) + + plot = plot_output.copy() + for key,value in plot.items(): + plot[key] = value.value + + match output_gates.value: + case 'VTG' if check_values(Vds_output,'primary') and check_values(Vtg_output,'secondary'): + smu_t=device.smu_dict() + smu_t.update(vname ='VTG',iname = 'ITG',mode = 'V',func='VAR2') + + smu_b= device.smu_dict() + smu_b.update(vname='VBG',iname='IBG',mode = 'COMM',func='CONS') + + points = number_of_points(Vtg_output) + + var2=device.var2_dict() + var2.update( + start=Vtg_output['start'].value, + step=Vtg_output['step'].value, + points=points, + comp=Vtg_output['comp'].value, + pcomp=Vtg_output['pcomp'].value, + stop = Vtg_output['stop'].value + ) + + meas_dict = { + "SAMPLE":sample_copy, + "MAP" : map, + "SMU_T":smu_t, + "SMU_D": smu_d, + "SMU_B":smu_b, + "SMU_S":smu_s, + "VAR1":var1, + "VAR2":var2, + "PLOT": plot, # plot in the tool + "INTEGRATION": integration_output.value, + } + + #measure later + Output_VTG(device,meas_dict) + + case 'VBG' if check_values(Vds_output,'primary') and check_values(Vbg_output,'secondary'): + smu_t=device.smu_dict() + smu_t.update(vname ='VTG',iname = 'ITG',mode = 'COMM',func='CONS') + + smu_b= device.smu_dict() + smu_b.update(vname='VBG',iname='IBG',mode = 'V',func = 'VAR2') + + points = number_of_points(Vbg_output) + + var2=device.var2_dict() + var2.update( + start=Vbg_output['start'].value, + step=Vbg_output['step'].value, + points=points, + comp=Vbg_output['comp'].value, + pcomp=Vbg_output['pcomp'].value, + stop = Vbg_output['stop'].value + ) + + meas_dict = { + "SAMPLE":sample_copy, + "MAP" : map, + "SMU_T":smu_t, + "SMU_D": smu_d, + "SMU_B":smu_b, + "SMU_S":smu_s, + "VAR1":var1, + "VAR2":var2, + "PLOT": plot, # plot in the tool + "INTEGRATION": integration_output.value, + } + + #measure later + Output_VBG(device,meas_dict) + + + case 'BOTH' if check_values(Vds_output,'primary') and check_values(Vtg_output,'secondary') and check_values(Vbg_output,'secondary'): + smu_t=device.smu_dict() + smu_t.update(vname ='VTG',iname = 'ITG',mode = 'V',func='VAR2') + + smu_b= device.smu_dict() + smu_b.update(vname='VBG',iname='IBG',mode = 'V',func = 'CONS') + + points = number_of_points(Vtg_output) + + var2=device.var2_dict() + var2.update( + start=Vtg_output['start'].value, + step=Vtg_output['step'].value, + points=points, + comp=Vtg_output['comp'].value, + pcomp=Vtg_output['pcomp'].value, + stop = Vtg_output['stop'].value + ) + + points_VBG = number_of_points(Vbg_output) + values_VBG = np.linspace(Vbg_output["start"].value,Vbg_output["stop"].value,num = points_VBG,endpoint= True) + + #there is not such unit we create it with a loop + var3 = { + "start":Vbg_output['start'].value, + "step":Vbg_output['step'].value, + "points":points_VBG, + "comp":Vbg_output['comp'].value, + "stop" : Vbg_output['stop'].value, + "values" : values_VBG + } + meas_dict = { + "SAMPLE":sample_copy, + "MAP" : map, + "SMU_T":smu_t, + "SMU_D": smu_d, + "SMU_B":smu_b, + "SMU_S":smu_s, + "VAR1":var1, + "VAR2":var2, + "VAR3":var3, + "PLOT": plot, # plot in the tool + "INTEGRATION": integration_output.value, + } + + #measure later + Output_BOTH(device,meas_dict) + + case _ : + information_box("Output Measurement skipped due to invalid parameters") + + + if gatediode_check.value == True: + #drain is disabled + device.smu_disable(map['D']) + + # VAR1 is set with the same interface + var1 = device.var1_dict() + var1.update( + mode=Vg_gatediode['hyst'].value, + start=Vg_gatediode['start'].value, + stop=Vg_gatediode['stop'].value, + step=Vg_gatediode['step'].value, + comp =Vg_gatediode['comp'].value, + pcomp=Vg_gatediode['pcomp'].value + ) + + plot = plot_gatediode.copy() + for key,value in plot.items(): + plot[key] = value.value + + match terminal.value: + case 'VTG' if check_values(Vg_gatediode,'primary'): + smu_t=device.smu_dict() + smu_t.update(vname ='VTG',iname = 'ITG',mode = 'V',func='VAR1') + + meas_dict = { + "SAMPLE":sample_copy, + "MAP" : map, + "SMU_T":smu_t, + "SMU_S":smu_s, + "VAR1":var1, + "PLOT": plot, # plot in the tool + "INTEGRATION": integration_gatediode.value, + } + #measure later + Gatediode_VTG(device,meas_dict) + + case 'VBG' if check_values(Vg_gatediode,'primary'): + smu_b=device.smu_dict() + smu_b.update(vname ='VBG',iname = 'IBG',mode = 'V',func='VAR1') + + meas_dict = { + "SAMPLE":sample_copy, + "MAP" : map, + "SMU_B":smu_b, + "SMU_S":smu_s, + "VAR1":var1, + "PLOT": plot, # plot in the tool + "INTEGRATION": integration_gatediode.value, + } + #measure later + Gatediode_VBG(device,meas_dict) + + case _ : + information_box("Gatediode Measurement skipped due to invalid parameters") + + information_box("Measurement finished!") + enable_widgets(all_widgets) + + +def on_export_ini_clicked(b): + with output: + disable_widgets(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',integration_transfer.value) + + config.add_section("Vtg_transfer") + for parameter,widget in Vtg_transfer.items(): + config.set('Vtg_transfer',parameter,str(widget.value)) + + config.add_section("Vbg_transfer") + for parameter,widget in Vbg_transfer.items(): + config.set('Vbg_transfer',parameter,str(widget.value)) + + config.add_section('Vds_transfer') + for parameter,widget in Vds_transfer.items(): + config.set('Vds_transfer',parameter,str(widget.value)) + + config.add_section('Plot_transfer') + for parameter,widget in plot_transfer.items(): + config.set('Plot_transfer',parameter,str(widget.value)) + + #output + config.add_section('Output') + config.set('Output','Integration',integration_output.value) + + config.add_section("Vtg_output") + for parameter,widget in Vtg_output.items(): + config.set('Vtg_output',parameter,str(widget.value)) + + config.add_section("Vbg_output") + for parameter,widget in Vbg_output.items(): + config.set('Vbg_output',parameter,str(widget.value)) + + config.add_section('Vds_output') + for parameter,widget in Vds_output.items(): + config.set('Vds_output',parameter,str(widget.value)) + + config.add_section('Plot_output') + for parameter,widget in plot_output.items(): + config.set('Plot_output',parameter,str(widget.value)) + + # Gatediode + config.add_section('Gatediode') + config.set('Gatediode','Integration',integration_gatediode.value) + + config.add_section("Vg_gatediode") + for parameter,widget in Vg_gatediode.items(): + config.set('Vg_gatediode',parameter,str(widget.value)) + + config.add_section('Plot_gatediode') + for parameter,widget in plot_gatediode.items(): + config.set('Plot_gatediode',parameter,str(widget.value)) + + config.write(configfile) + except Exception as e: + information_box(e) + + enable_widgets(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) + + #transfer curve + integration_transfer.value = config.get('Transfer', "integration") + for parameter,widget in Vtg_transfer.items(): + widget.value = config.get('Vtg_transfer',parameter) + for parameter,widget in Vds_transfer.items(): + widget.value = config.get('Vds_transfer',parameter) + for parameter,widget in Vbg_transfer.items(): + widget.value = config.get('Vbg_transfer',parameter) + for parameter,widget in plot_transfer.items(): + widget.value = config.get('Plot_transfer',parameter) + + #output curve + integration_output.value = config.get('Output','integration') + for parameter,widget in Vtg_output.items(): + widget.value = config.get('Vtg_output',parameter) + for parameter,widget in Vds_output.items(): + widget.value = config.get('Vds_output',parameter) + for parameter,widget in Vbg_output.items(): + widget.value = config.get('Vbg_output',parameter) + + for parameter,widget in plot_output.items(): + widget.value = config.get('Plot_output',parameter) + + # gatediode + integration_gatediode.value = config.get('Gatediode','integration') + for parameter,widget in Vg_gatediode.items(): + widget.value = config.get('Vg_gatediode',parameter) + + for parameter,widget in plot_gatediode.items(): + widget.value = config.get('Plot_gatediode',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") + enable_widgets(all_widgets) + + +button.on_click(on_start_clicked) +import_ini_button.on_click(on_import_ini_clicked) +export_ini_button.on_click(on_export_ini_clicked) + + + + + + + + + + + diff --git a/hp4155/ADU for double gate devices-test/double_gate_ADU_interface.ipynb b/hp4155/ADU for double gate devices-test/double_gate_ADU_interface.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..cecc7c917d748fd81e29c5cc2b8906c2a2e6cb33 --- /dev/null +++ b/hp4155/ADU for double gate devices-test/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": "e555573e22c546a6a305096c9d476115", + "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": "d1459686805d4489854221f8fd6a9718", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(Checkbox(value=True, description='Transfer Curve', indent=False), Checkbox(value=True, descript…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "4c7ddbf240104c3bab678fccc26db0df", + "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": "57fedee09ea547dcb6ebaae046057df3", + "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": "0e81c985ca234bc3bde9f042ae86921a", + "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": "40d5f341-bc61-4698-8bdd-f99e94f4e325", + "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-test/lib/help.py b/hp4155/ADU for double gate devices-test/lib/help.py new file mode 100644 index 0000000000000000000000000000000000000000..265b43fd3a81f8a615c5ae8fdabb20102dbe1227 --- /dev/null +++ b/hp4155/ADU for double gate devices-test/lib/help.py @@ -0,0 +1,303 @@ +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 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(dict): + try: + diff = dict['stop'].value - dict['start'].value + ratio = abs(diff/dict['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 check_values(dictionary,function): + 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 function =='primary': + if abs(dictionary['step'].value) > abs(dictionary['stop'].value-dictionary['start'].value) or dictionary['step'].value==0:#invalid parameter setting + valid = False + tkinter.messagebox.showerror(message="Invalid parameter setting!") + + if dictionary['start'].value<dictionary['step'].value and dictionary['step'].value<0: #change polarity + dictionary['step'].value =(-1)*dictionary['step'].value + + elif dictionary['start'].value>dictionary['stop'].value and dictionary['step'].value>0: + dictionary['step'].value = (-1)*dictionary['step'].value + + else: + pass + + if function == 'secondary': + if dictionary['start'].value == dictionary['stop'].value: + pass + elif abs(dictionary['step'].value) > abs(dictionary['stop'].value-dictionary['start'].value) or dictionary['step'].value==0:#invalid parameter setting + valid = False + tkinter.messagebox.showerror(message="Invalid parameter setting!") + if dictionary['start'].value<dictionary['step'].value and dictionary['step'].value<0: #change polarity + dictionary['step'].value =(-1)*dictionary['step'].value + + elif dictionary['start'].value>dictionary['stop'].value and dictionary['step'].value>0: + dictionary['step'].value = (-1)*dictionary['step'].value + + if function == 'synchronous': + pass + + if valid == True: + #check compliance + comp = dictionary['comp'].value + start = dictionary['start'].value + stop = dictionary['stop'].value + + if abs(comp)*max(abs(start),abs(stop))>2: + dictionary["comp"].value=np.sign(comp)*2/max(abs(start),abs(stop)) + + root.destroy() + return valid + +def add_widgets_to_list(source_dictionary,target_list): + for widget in source_dictionary.values(): + target_list.append(widget) + +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 + +def disable_widgets(widgets_list): + for widget in widgets_list: + widget.disabled = True + +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): + 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(smu_map:dict): + #convert the dictionaries values to a list + + map_list = [] + for element in smu_map.values(): + map_list.append(element.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(params,device): + device.delete_axis("X") + device.delete_axis("Y1") + device.delete_axis("Y2") + device.del_user_functions() + + device.clear_error_stack() + + # How to define user functions correctly and not multiple times + plot_list = [params["PLOT"]["x"],params["PLOT"]["y1"],params["PLOT"]["y2"]] + 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(params['SAMPLE']['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',params["PLOT"]["x"]) + device.error_occured() + device.axis_scale('X','LIN') + device.error_occured() + device.display_variable_min_max('X','MIN',params["PLOT"]["x_min"]) + device.error_occured() + device.display_variable_min_max('X','MAX',params["PLOT"]["x_max"]) + device.error_occured() + + + if params["PLOT"]["y1_scale"]=='LOG': + device.display_variable('Y1',"A"+params["PLOT"]["y1"]) + else: + device.display_variable('Y1',params["PLOT"]["y1"]) + device.error_occured() + device.axis_scale('Y1',params["PLOT"]["y1_scale"]) + device.error_occured() + device.display_variable_min_max('Y1','MIN',params["PLOT"]["y1_min"]) + device.error_occured() + device.display_variable_min_max('Y1','MAX',params["PLOT"]["y1_max"]) + device.error_occured() + + if params["PLOT"]["y2"]!= "None": + if params["PLOT"]["y2_scale"]=='LOG': + device.display_variable('Y2',"A"+params["PLOT"]["y2"]) + else: + device.display_variable('Y2',params["PLOT"]["y2"]) + device.error_occured() + device.axis_scale('Y2',params["PLOT"]["y2_scale"]) + device.error_occured() + device.display_variable_min_max('Y2','MIN',params["PLOT"]["y2_min"]) + device.display_variable_min_max('Y2','MAX',params["PLOT"]["y2_max"]) + device.error_occured() + + + +#plot software functions +def values_to_plot(params,device): + #plotted variables as they are named in the tool + #return the plotted data for easier configuration + plotted_variables = {'X':device.get_axis_variable('X'),'Y1': device.get_axis_variable('Y1')} + + if params["PLOT"]["y2"]!= "None": + plotted_variables['Y2']= device.get_axis_variable('Y2') + + plot_values ={} + for axis,variable in plotted_variables.items(): + plot_values.setdefault(axis,np.array(device.return_values(variable))) + + return plot_values + +def set_axes_labels(plot_list): + axes_labels=[] + + # define the axes labels similarly to the user functions + for element in plot_list: + if element != "None": #only the last one + if element.startswith("V"): + label = f"{element} (V)" + elif element.startswith('I') and element.endswith('mm'): + label = f"{element[:-2]} (uA/um)" + else: # regular I + label = f"{element} (A)" + axes_labels.append(label) + + return axes_labels + + + + + + + + + \ No newline at end of file diff --git a/hp4155/ADU for double gate devices-test/lib/interface.py b/hp4155/ADU for double gate devices-test/lib/interface.py new file mode 100644 index 0000000000000000000000000000000000000000..8cdca3cd6da686ea4d9610a4b9dbc7a29fb6ff86 --- /dev/null +++ b/hp4155/ADU for double gate devices-test/lib/interface.py @@ -0,0 +1,340 @@ +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%" + +def header(name,integration): + style = {'description_width': 'initial'} + options_integration=["SHORt","MEDium","LONG"] + + check=widgets.Checkbox( + description = name, + value = True, + indent = False + ) + integration= widgets.Dropdown( + options=options_integration, + value=integration,description='Integration Time', + style =style, + layout=Layout(height='auto', width="30%") + ) + + select =widgets.Dropdown( + options = ['VTG','VBG',"BOTH"], + description = 'Sweeping Gates:', + value ='BOTH', + style= {'description_width': 'initial'} + ) + + return check, integration ,select + + +def primary(name,start,step,stop,comp): + primary_grid = GridspecLayout(4,4) + primary_grid[:,3]=widgets.Label(name,layout=Layout(height='auto', width='auto')) + primary_grid[:,3].style.font_weight = 'bold' + + + #first line + primary_grid[0,0]=widgets.Label("Start(V)",layout=Layout(height='auto', width='auto')) + primary_grid[0,1]=widgets.Label("Step(V)",layout=Layout(height='auto', width='auto')) + primary_grid[0,2]=widgets.Label("Stop(V)",layout=Layout(height='auto', width='auto')) + + #second line + primary_grid[1,0]=widgets.BoundedFloatText(value=start,min=-100,max=100,step=1,layout=Layout(height='auto', width=floatbox_width)) + primary_grid[1,1]=widgets.BoundedFloatText(value=step,min=-200,max=200,step=1,layout=Layout(height='auto', width=floatbox_width)) + primary_grid[1,2]=widgets.BoundedFloatText(value=stop,min=-100,max=100,step=1,layout=Layout(height='auto', width=floatbox_width)) + + #third line + primary_grid[2,0]=widgets.Label("Compliance(A)",layout=Layout(height='auto', width='auto')) + primary_grid[2,1] =widgets.Label("Power Compliance(W)(0=OFF)",layout=Layout(height='auto', width='auto'))#mind the gap + primary_grid[2,2] =widgets.Label("Hysterisis",layout=Layout(height='auto', width='auto'))#mind the gap + + #fourth line + primary_grid[3,0]=widgets.BoundedFloatText(value=comp,min=-0.1,max=0.1,step=0.01,layout=Layout(height='auto', width=floatbox_width)) + primary_grid[3,1]=widgets.BoundedFloatText(value=0,min=0,max=2,step=0.1,layout=Layout(height='auto', width=floatbox_width))#mind the gap + primary_grid[3,2]=widgets.Dropdown(options=['SINGle','DOUBle'],value='SINGle',layout=Layout(height='auto', width=floatbox_width))#mind the gap + + + parameters = { + 'start': primary_grid[1,0], + 'step': primary_grid[1,1], + 'stop': primary_grid[1,2], + 'comp': primary_grid[3,0], + 'hyst':primary_grid[3,2], + 'pcomp':primary_grid[3,1] + } + return primary_grid,parameters + +def secondary(name,start,step,stop,comp): + secondary_grid = GridspecLayout(4,4) + secondary_grid[:,3]=widgets.Label(name,layout=Layout(height='auto', width='auto')) + secondary_grid[:,3].style.font_weight = 'bold' + + #first line + secondary_grid[0,0]=widgets.Label("Start(V)",layout=Layout(height='auto', width='auto')) + secondary_grid[0,1]=widgets.Label("Step(V)",layout=Layout(height='auto', width='auto')) + secondary_grid[0,2]=widgets.Label("Stop(V)",layout=Layout(height='auto', width='auto')) + + #second line + secondary_grid[1,0]=widgets.BoundedFloatText(value=start,min=-100,max=100,step=1,layout=Layout(height='auto', width=floatbox_width)) + secondary_grid[1,1]=widgets.BoundedFloatText(value=step,min=-200,max=200,step=1,layout=Layout(height='auto', width=floatbox_width)) + secondary_grid[1,2]=widgets.BoundedFloatText(value=stop,min=-100,max=100,step=1,layout=Layout(height='auto', width=floatbox_width)) + + #third line + secondary_grid[2,0]=widgets.Label("Compliance(A)",layout=Layout(height='auto', width='auto')) + secondary_grid[2,2] =widgets.Label("Power Compliance(W)(0=OFF)",layout=Layout(height='auto', width='auto'))#mind the gap + + #fourth line + secondary_grid[3,0]=widgets.BoundedFloatText(value=comp,min=-0.1,max=0.1,step=0.01,layout=Layout(height='auto', width=floatbox_width)) + secondary_grid[3,2]=widgets.BoundedFloatText(value=0,min=0,max=2,step=0.1,layout=Layout(height='auto', width=floatbox_width))#mind the gap + + parameters = { + 'start': secondary_grid[1,0], + 'step': secondary_grid[1,1], + 'stop':secondary_grid[1,2], + 'comp':secondary_grid[3,0], + 'pcomp':secondary_grid[3,2] + } + + + return secondary_grid,parameters + + +def information_box_new(): + width = '90%' + sample_information=widgets.Label("Sample Information",layout=Layout(height=height, width='50%')) + sample_information.style.font_weight='bold' + information_grid=GridspecLayout(3,2) + + for i in range(3): + for j in range(2): + if i ==2 and j == 1: + information_grid[i,j]=widgets.Checkbox(value = True,indent = False) + elif i == 2 and j == 0: + information_grid[i,j]=widgets.BoundedFloatText( + value=100, + min=1e-3, + max=sys.float_info.max,step=1, + layout=Layout(height=height, width=width) + ) + else: + information_grid[i,j]=widgets.Text(layout=Layout(height=height, width=width)) + + information_grid[0,0].description = "Processing-Nr:" + information_grid[1,0].description = "Sample:" + information_grid[2,0].description = "Device Width(um):" + information_grid[0,1].description = "Field(XYY):" + information_grid[1,1].description = "Device:" + information_grid[2,1].description ='Save Plots' + + for i in range(3): + for j in range(2): + information_grid[i,j].style = style + + + + config = widgets.Label("SMU Configuration",layout=Layout(height='auto', width='auto')) + + top_gate = widgets.Dropdown(options=[1,2,3,4],value=1,layout=Layout(height='auto', width='auto'),description = 'Top Gate:',style = style) + drain = widgets.Dropdown(options=[1,2,3,4],value=2,layout=Layout(height='auto', width='auto'),description = 'Drain:',style = style) + back_gate = widgets.Dropdown(options=[1,2,3,4],value=3,layout=Layout(height='auto', width='auto'),description = 'Back Gate:',style = style) + source = widgets.Dropdown(options=[1,2,3,4],value=4,layout=Layout(height='auto', width='auto'),description = 'Source(Ground):',style = style) + + + vbox2 = widgets.VBox([config,top_gate,drain,back_gate,source]) + vbox1=widgets.VBox([sample_information,information_grid]) + display(widgets.HBox([vbox1,vbox2])) + + information = { + 'processing_number': information_grid[0,0], + 'sample' : information_grid[1,0], + 'field': information_grid[0,1], + 'device':information_grid[1,1], + 'width': information_grid[2,0], + 'save_fig':information_grid[2,1] + } + + smu_assign ={ + 'TG':top_gate, + 'D':drain, + 'S':source, + 'BG':back_gate + } + + return information,smu_assign + +def synchronous(name,start,step,stop,comp): + synchronous_grid = GridspecLayout(4,4) + synchronous_grid[:,3]=widgets.Label(name,layout=Layout(height='auto', width='auto')) + synchronous_grid[:,3].style.font_weight = 'bold' + + + #first line + synchronous_grid[0,0]=widgets.Label("Start(V)",layout=Layout(height='auto', width='auto')) + synchronous_grid[0,1]=widgets.Label("Step(V)(Only 1 Gate)",layout=Layout(height='auto', width='auto'),style = style) + synchronous_grid[0,2]=widgets.Label("Stop(V)",layout=Layout(height='auto', width='auto')) + + #second line + synchronous_grid[1,0]=widgets.BoundedFloatText(value=start,min=-100,max=100,step=1,layout=Layout(height='auto', width=floatbox_width)) + synchronous_grid[1,1]=widgets.BoundedFloatText(value=step,min=-200,max=200,step=1,layout=Layout(height='auto', width=floatbox_width)) + synchronous_grid[1,2]=widgets.BoundedFloatText(value=stop,min=-100,max=100,step=1,layout=Layout(height='auto', width=floatbox_width)) + + #third line + synchronous_grid[2,0]=widgets.Label("Compliance(A)",layout=Layout(height='auto', width='auto')) + synchronous_grid[2,1] =widgets.Label("Power Compliance(W)(0=OFF)",layout=Layout(height='auto', width='auto'),style = style)#mind the gap + synchronous_grid[2,2] =widgets.Label("Hysterisis(Only 1 gate)",layout=Layout(height='auto', width='auto'),style = style)#mind the gap + + #fourth line + synchronous_grid[3,0]=widgets.BoundedFloatText(value=comp,min=-0.1,max=0.1,step=0.01,layout=Layout(height='auto', width=floatbox_width)) + synchronous_grid[3,1]=widgets.BoundedFloatText(value=0,min=0,max=2,step=0.1,layout=Layout(height='auto', width=floatbox_width))#mind the gap + synchronous_grid[3,2]=widgets.Dropdown(options=['SINGle','DOUBle'],value='SINGle',layout=Layout(height='auto', width=floatbox_width))#mind the gap + + + parameters = { + 'start': synchronous_grid[1,0], + 'stop': synchronous_grid[1,2], + 'comp': synchronous_grid[3,0], + 'pcomp':synchronous_grid[3,1], + 'step': synchronous_grid[1,1], + 'hyst': synchronous_grid[3,2] + } + return synchronous_grid,parameters + +def additional_secondary(name,start,step,stop,comp): + secondary_grid = GridspecLayout(4,4) + secondary_grid[:,3]=widgets.Label(name,layout=Layout(height='auto', width='auto')) + secondary_grid[:,3].style.font_weight = 'bold' + + #first line + secondary_grid[0,0]=widgets.Label("Start(V)",layout=Layout(height='auto', width='auto')) + secondary_grid[0,1]=widgets.Label("Step(V)",layout=Layout(height='auto', width='auto')) + secondary_grid[0,2]=widgets.Label("Stop(V)",layout=Layout(height='auto', width='auto')) + + #second line + secondary_grid[1,0]=widgets.BoundedFloatText(value=start,min=-100,max=100,step=1,layout=Layout(height='auto', width=floatbox_width)) + secondary_grid[1,1]=widgets.BoundedFloatText(value=step,min=-200,max=200,step=1,layout=Layout(height='auto', width=floatbox_width)) + secondary_grid[1,2]=widgets.BoundedFloatText(value=stop,min=-100,max=100,step=1,layout=Layout(height='auto', width=floatbox_width)) + + #third line + secondary_grid[2,0]=widgets.Label("Compliance(A)",layout=Layout(height='auto', width='auto')) + secondary_grid[2,2] =widgets.Label("Power Compliance(W)(0=OFF)(Only 1 Gate)",layout=Layout(height='auto', width='auto'),style = style)#mind the gap + + #fourth line + secondary_grid[3,0]=widgets.BoundedFloatText(value=comp,min=-0.1,max=0.1,step=0.01,layout=Layout(height='auto', width=floatbox_width)) + secondary_grid[3,2]=widgets.BoundedFloatText(value=0,min=0,max=2,step=0.1,layout=Layout(height='auto', width=floatbox_width))#mind the gap + + parameters = { + 'start': secondary_grid[1,0], + 'step': secondary_grid[1,1], + 'stop':secondary_grid[1,2], + 'comp':secondary_grid[3,0], + 'pcomp':secondary_grid[3,2] + } + return secondary_grid,parameters + + +def plot_config(meas): #meas = 1,2,3 for transfer,output,gatediode + config_grid = GridspecLayout(6,4) + + if meas== 3: + options_x= ['VTG','VBG'] + else : + options_x = ['VTG','VBG','VDS'] + + 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 + + + config_grid[0,:]=widgets.Label('Send Plotting configurations to the tool',layout=Layout(height='auto', width='auto')) + #first line headers + config_grid[1,1]= widgets.Label('X',layout=Layout(height='auto', width='auto')) + config_grid[1,2]= widgets.Label('Y1',layout=Layout(height='auto', width='auto')) + config_grid[1,3]= widgets.Label('Y2',layout=Layout(height='auto', width='auto')) + + #first column + config_grid[2,0] = widgets.Label('NAME',layout=Layout(height='auto', width='auto')) + config_grid[3,0] = widgets.Label('SCALE',layout=Layout(height='auto', width='auto')) + config_grid[4,0] = widgets.Label('MIN',layout=Layout(height='auto', width='auto')) + config_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'] + config_grid[2,1]=widgets.Dropdown(layout=Layout(height='auto', width='auto'),options = options_x,value = x_name) + config_grid[2,2]=widgets.Dropdown(layout=Layout(height='auto', width='auto'),options = must_options,value = y1_name) + config_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'] + config_grid[3,1] = widgets.Dropdown(value = x_scale,options = options_scale, layout=Layout(height='auto', width='auto'),disabled = True) + config_grid[3,2] = widgets.Dropdown(value = y1_scale,options = options_scale, layout=Layout(height='auto', width='auto')) + config_grid[3,3] = widgets.Dropdown(value = 'LIN',options = options_scale, layout=Layout(height='auto', width='auto')) + + #iterate throuh the last 2 lines(min-max) + + config_grid[4,1] = widgets.FloatText(value = x_min,layout=Layout(height='auto', width='auto')) #min + config_grid[4,2] = widgets.FloatText(value = y1_min,layout=Layout(height='auto', width='auto')) #min + config_grid[4,3] = widgets.FloatText(value = 0,layout=Layout(height='auto', width='auto')) #min + + config_grid[5,1]=widgets.FloatText(value = x_max,layout=Layout(height='auto', width='auto')) #max X-axis + config_grid[5,2]=widgets.FloatText(value = y1_max,layout=Layout(height='auto', width='auto')) #max Y1-axis + config_grid[5,3]=widgets.FloatText(value = 0,layout=Layout(height='auto', width='auto')) #max Y2-axis + + config_dict = { + "x":config_grid[2,1], + "y1":config_grid[2,2], + "y2":config_grid[2,3], + #"x_scale":config_grid[3,1], + "y1_scale":config_grid[3,2], + "y2_scale":config_grid[3,3], + "x_min":config_grid[4,1], + "y1_min":config_grid[4,2], + "y2_min":config_grid[4,3], + "x_max":config_grid[5,1], + "y1_max":config_grid[5,2], + "y2_max":config_grid[5,3], + } + + return config_grid,config_dict + + + \ No newline at end of file diff --git a/hp4155/ADU for double gate devices-test/lib/measurements.py b/hp4155/ADU for double gate devices-test/lib/measurements.py new file mode 100644 index 0000000000000000000000000000000000000000..8550c619fc2402889a4e26229e7e28ecf249587a --- /dev/null +++ b/hp4155/ADU for double gate devices-test/lib/measurements.py @@ -0,0 +1,1229 @@ +# New measurements file for ADU + +# The new measurements will have the smus configuration as a parameter and written from the interface + +import sys +sys.path.insert(0, '..') #append parent directory + +import hp4155a +from help import * +from decimal import Decimal + +import os + +def Setup(device): + device.reset() + + #setup sweep measurement mode + device.measurement_mode('SWE') + + #disable all irrelevant units + device.disable_not_smu() + +# Transfer only VTG +def Transfer_VTG(device,params): + # calculate normalization factor + + norm = normalization_factor(params["SAMPLE"]["width"]) + points = params["VAR2"]["points"] + try: + device.setup_smu(params["MAP"]['TG'],params["SMU_T"]) + device.setup_smu(params["MAP"]['D'],params["SMU_D"]) + device.setup_smu(params["MAP"]['BG'],params["SMU_B"]) + device.setup_smu(params["MAP"]['S'],params["SMU_S"]) + + device.setup_var1(params["VAR1"]) + device.setup_var2(params["VAR2"]) + + device.integration_time(params["INTEGRATION"]) + + variables_list =["VTG","ITG","VDS","ID"] + device.variables_to_save(variables_list) + + try: + plotted_variables = graph_tool(params,device) + except Exception as e: + error_box(e) + return + + + device.single_measurement() + while device.operation_completed()==False: + pass + + device.autoscaling() + device.error_occured() + + 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"{params['SAMPLE']['sample']}_{params['SAMPLE']['field']}_{params['SAMPLE']['device']}_TOP_GATE_U.txt" + + 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 =default_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() + + with open(file,'w') as f: + date = str(datetime.today().replace(microsecond=0)) + f.write(f"Transfer Curve at {date}"+"\n") + f.write(f"Series:{params['SAMPLE']['processing_number']}"+"\n") + f.write(f"Sample:{params['SAMPLE']['sample']}"+"\n") + f.write(f"Field:{params['SAMPLE']['field']}"+"\n") + f.write(f"Device:{params['SAMPLE']['device']}"+"\n") + f.write(f"Device Width/um:{params['SAMPLE']['width']}"+"\n") + f.write("Sweeping Gate:VTG"+"\n\n") + + f.write('Parameters\n') + f.write(f"VTG from {params['VAR1']['start']}V to {params['VAR1']['stop']}V with step {params['VAR1']['step']}V"+"\n") + f.write(f"VDS from {params['VAR2']['start']}V to {params['VAR2']['stop']}V with step {params['VAR2']['step']}V"+"\n") + + if params['VAR1']['pcomp']==0: + f.write(f"Top Gate Current Compliance/A:{params['VAR1']['comp']}"+"\n") + else: + f.write(f"Top Gate Power Compliance/A:{params['VAR1']['pcomp']}"+"\n") + + if params['VAR2']['pcomp'] == 0: + f.write(f"Drain Current Compliance/A:{params['VAR1']['comp']}"+"\n") + else: + f.write(f"Drain Power Compliance/A:{params['VAR1']['pcomp']}"+"\n") + f.write(f"Integration Time:{params['INTEGRATION']}"+"\n") + f.write("\nResults\n") + + df.to_csv(file,sep=" ",mode='a') + + plot_values = values_to_plot(params,device) + + # Plot user specified results + fig,ax1= plt.subplots(figsize=(10,6),layout='constrained') + + plot_list = [params["PLOT"]["x"],params["PLOT"]["y1"],params["PLOT"]["y2"]] + scale_list =['LIN',params["PLOT"]["y1_scale"],params["PLOT"]["y2_scale"]] + axes_labels = set_axes_labels(plot_list) + + if scale_list[1]=='LOG': + ax1.set_yscale('log') + + #now set the labels + ax1.set_xlabel(axes_labels[0]) + ax1.set_ylabel(axes_labels[1]) + + x = np.split(plot_values['X'],points) + y1 = np.split(plot_values['Y1'],points) + labels =np.mean(np.array_split(df["VDS/V"],points),axis = 1) # VDS values for labels + + for i in range(points): + ax1.plot(x[i],y1[i],label = f"VDS:{round(labels[i],3)} (V)") + + # Adding title + fig.suptitle('Transfer Curve', fontweight ="bold") + fig.legend(loc='outside right upper') + + display(fig) + #save plot if checked + if params["SAMPLE"]['save_fig'] == True: + filename= os.path.splitext(file)[0] + fig.savefig(filename+'_Y1.png') + + #add the second axis if applicable + if plot_list[2]!= "None": + fig,ax2=plt.subplots(figsize=(10,6),layout='constrained') + y2 = np.split(plot_values['Y2'],points) + + if scale_list[2]=='LOG': + ax2.set_yscale('log') + + ax2.set_xlabel(axes_labels[0]) + + ax2.set_ylabel(axes_labels[2]) + for i in range(points): + ax2.plot(x[i],y2[i],label = f"VDS:{round(labels[i],3)} (V)") + + # Adding title + fig.suptitle('Transfer Curve', fontweight ="bold") + fig.legend(loc='outside right upper') + + display(fig) + #save plot if checked + if params["SAMPLE"]['save_fig'] == True: + filename= os.path.splitext(file)[0] + fig.savefig(filename+'_Y2.png') + + +# Transfer only VBG +def Transfer_VBG(device,params): + try: + device.setup_smu(params["MAP"]['TG'],params["SMU_T"]) + device.setup_smu(params["MAP"]['D'],params["SMU_D"]) + device.setup_smu(params["MAP"]['BG'],params["SMU_B"]) + device.setup_smu(params["MAP"]['S'],params["SMU_S"]) + + + device.setup_var1(params["VAR1"]) + device.setup_var2(params["VAR2"]) + + device.integration_time(params["INTEGRATION"]) + + + variables_list =["VBG","IBG","VDS","ID"] + device.variables_to_save(variables_list) + try: + plotted_variables = graph_tool(params,device) + except Exception as e: + error_box(e) + return + + device.single_measurement() + while device.operation_completed()==False: + pass + device.autoscaling() + device.error_occured() + + 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 + # calculate normalization factor + norm = normalization_factor(params["SAMPLE"]["width"]) + points = params["VAR2"]["points"] + + # 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"{params['SAMPLE']['sample']}_{params['SAMPLE']['field']}_{params['SAMPLE']['device']}_BACK_GATE_U.txt" + + 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 =default_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() + + with open(file,'w') as f: + date = str(datetime.today().replace(microsecond=0)) + f.write(f"Transfer Curve at {date}"+"\n") + f.write(f"Series:{params['SAMPLE']['processing_number']}"+"\n") + f.write(f"Sample:{params['SAMPLE']['sample']}"+"\n") + f.write(f"Field:{params['SAMPLE']['field']}"+"\n") + f.write(f"Device:{params['SAMPLE']['device']}"+"\n") + f.write(f"Device Width/um:{params['SAMPLE']['width']}"+"\n") + f.write("Sweeping Gate:VBG"+"\n\n") + + f.write('Parameters\n') + f.write(f"VBG from {params['VAR1']['start']}V to {params['VAR1']['stop']}V with step {params['VAR1']['step']}V"+"\n") + f.write(f"VDS from {params['VAR2']['start']}V to {params['VAR2']['stop']}V with step {params['VAR2']['step']}V"+"\n") + + #calculate the values + if params['VAR1']['pcomp']==0: + f.write(f"Back Gate Current Compliance/A:{params['VAR1']['comp']}"+"\n") + else: + f.write(f"Back Gate Power Compliance/A:{params['VAR1']['pcomp']}"+"\n") + + if params['VAR2']['pcomp'] == 0: + f.write(f"Drain Current Compliance/A:{params['VAR2']['comp']}"+"\n") + else: + f.write(f"Drain Power Compliance/A:{params['VAR2']['pcomp']}"+"\n") + + + f.write(f"Integration Time:{params['INTEGRATION']}"+"\n") + + f.write("\nResults\n") + + df.to_csv(file,sep=" ",mode='a') + + plot_values = values_to_plot(params,device) + + # Plot user specified results + fig,ax1= plt.subplots(figsize=(10,6),layout='constrained') + + plot_list = [params["PLOT"]["x"],params["PLOT"]["y1"],params["PLOT"]["y2"]] + scale_list =['LIN',params["PLOT"]["y1_scale"],params["PLOT"]["y2_scale"]] + axes_labels = set_axes_labels(plot_list) + + if scale_list[1]=='LOG': + ax1.set_yscale('log') + + #now set the labels + ax1.set_xlabel(axes_labels[0]) + ax1.set_ylabel(axes_labels[1]) + + x = np.split(plot_values['X'],points) + y1 = np.split(plot_values['Y1'],points) + labels =np.mean(np.array_split(df["VDS/V"],points),axis = 1) # VDS values for labels + + for i in range(points): + ax1.plot(x[i],y1[i],label = f"VDS:{round(labels[i],3)} (V)") + + # Adding title + fig.suptitle('Transfer Curve', fontweight ="bold") + fig.legend(loc='outside right upper') + + display(fig) + #save plot if checked + if params["SAMPLE"]['save_fig'] == True: + filename= os.path.splitext(file)[0] + fig.savefig(filename+'_Y1.png') + + #add the second axis if applicable + if plot_list[2]!= "None": + fig,ax2=plt.subplots(figsize=(10,6),layout='constrained') + y2 = np.split(plot_values['Y2'],points) + + if scale_list[2]=='LOG': + ax2.set_yscale('log') + + ax2.set_xlabel(axes_labels[0]) + + ax2.set_ylabel(axes_labels[2]) + for i in range(points): + ax2.plot(x[i],y2[i],label = f"VDS:{round(labels[i],3)} (V)") + + # Adding title + fig.suptitle('Transfer Curve', fontweight ="bold") + fig.legend(loc='outside right upper') + + display(fig) + #save plot if checked + if params["SAMPLE"]['save_fig'] == True: + filename= os.path.splitext(file)[0] + fig.savefig(filename+'_Y2.png') + + +def Transfer_BOTH(device,params): + try: + smu_t = device.smu_dict() + smu_t.update(vname = 'VTG',iname='ITG',mode = 'V',func='VAR1') + + smu_b = device.smu_dict() + smu_b.update(vname = 'VBG',iname='IBG',mode = 'V',func = 'VARD') + + device.setup_smu(params["MAP"]['TG'],params["SMU_T"]) + device.setup_smu(params["MAP"]['D'],params["SMU_D"]) + device.setup_smu(params["MAP"]['BG'],params["SMU_B"]) + device.setup_smu(params["MAP"]['S'],params["SMU_S"]) + + + device.setup_var1(params["VAR1"]) + device.setup_var2(params["VAR2"]) + device.setup_vard(params["VARD"]) + + device.integration_time(params["INTEGRATION"]) + + variables_list =["VBG","IBG","VDS","ID","VTG","ITG"] + device.variables_to_save(variables_list) + + try: + plotted_variables = graph_tool(params,device) + except Exception as e: + error_box(e) + return + + + device.single_measurement() + while device.operation_completed()==False: + pass + + device.autoscaling() + device.error_occured() + + 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 + + # calculate normalization factor + norm = normalization_factor(params["SAMPLE"]["width"]) + points = params["VAR2"]["points"] + + # 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"{params['SAMPLE']['sample']}_{params['SAMPLE']['field']}_{params['SAMPLE']['device']}_BOTH_GATES_U.txt" + + 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 =default_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() + + + with open(file,'w') as f: + date = str(datetime.today().replace(microsecond=0)) + f.write(f"Transfer Curve at {date}"+"\n") + f.write(f"Series:{params['SAMPLE']['processing_number']}"+"\n") + f.write(f"Sample:{params['SAMPLE']['sample']}"+"\n") + f.write(f"Field:{params['SAMPLE']['field']}"+"\n") + f.write(f"Device:{params['SAMPLE']['device']}"+"\n") + f.write(f"Device Width/um:{params['SAMPLE']['width']}"+"\n") + f.write("Sweeping Gates:VBG,VTG"+"\n\n") + + f.write('Parameters\n') + f.write(f"VBG from {params['VARD']['start']}V to {params['VARD']['stop']}V with step {params['VARD']['step']}V"+"\n") + f.write(f"VTG from {params['VAR1']['start']}V to {params['VAR1']['stop']}V with step {params['VAR1']['step']}V"+"\n") + f.write(f"VDS from {params['VAR2']['start']}V to {params['VAR2']['stop']}V with step {params['VAR2']['step']}V"+"\n") + + #calculate thes + if params['VARD']['pcomp']==0: + f.write(f"Back Gate Current Compliance/A:{params['VARD']['comp']}"+"\n") + else: + f.write(f"Back Gate Power Compliance/A:{params['VARD']['pcomp']}"+"\n") + + if params['VAR1']['pcomp']==0: + f.write(f"Top Gate Current Compliance/A:{params['VAR1']['comp']}"+"\n") + else: + f.write(f"Top Gate Power Compliance/A:{params['VAR1']['pcomp']}"+"\n") + + if params['VAR2']['pcomp'] == 0: + f.write(f"Drain Current Compliance/A:{params['VAR2']['comp']}"+"\n") + else: + f.write(f"Drain Power Compliance/A:{params['VAR2']['pcomp']}"+"\n") + + f.write(f"Integration Time:{params['INTEGRATION']}"+"\n") + + f.write("\nResults\n") + + df.to_csv(file,sep=" ",mode='a') + plot_values = values_to_plot(params,device) + + # Plot user specified results + fig,ax1= plt.subplots(figsize=(10,6),layout='constrained') + + plot_list = [params["PLOT"]["x"],params["PLOT"]["y1"],params["PLOT"]["y2"]] + scale_list =['LIN',params["PLOT"]["y1_scale"],params["PLOT"]["y2_scale"]] + axes_labels = set_axes_labels(plot_list) + + if scale_list[1]=='LOG': + ax1.set_yscale('log') + + #now set the labels + ax1.set_xlabel(axes_labels[0]) + ax1.set_ylabel(axes_labels[1]) + + x = np.split(plot_values['X'],points) + y1 = np.split(plot_values['Y1'],points) + labels =np.mean(np.array_split(df["VDS/V"],points),axis = 1) # VDS values for labels + + for i in range(points): + ax1.plot(x[i],y1[i],label = f"VDS:{round(labels[i],3)} (V)") + + # Adding title + fig.suptitle('Transfer Curve', fontweight ="bold") + fig.legend(loc='outside right upper') + + display(fig) + #save plot if checked + if params["SAMPLE"]['save_fig'] == True: + filename= os.path.splitext(file)[0] + fig.savefig(filename+'_Y1.png') + + #add the second axis if applicable + if plot_list[2]!= "None": + fig,ax2=plt.subplots(figsize=(10,6),layout='constrained') + y2 = np.split(plot_values['Y2'],points) + + if scale_list[2]=='LOG': + ax2.set_yscale('log') + + ax2.set_xlabel(axes_labels[0]) + + ax2.set_ylabel(axes_labels[2]) + for i in range(points): + ax2.plot(x[i],y2[i],label = f"VDS:{round(labels[i],3)} (V)") + + # Adding title + fig.suptitle('Transfer Curve', fontweight ="bold") + fig.legend(loc='outside right upper') + + display(fig) + #save plot if checked + if params["SAMPLE"]['save_fig'] == True: + filename= os.path.splitext(file)[0] + fig.savefig(filename+'Y2.png') + +# Output with VTG +def Output_VTG(device,params): + try: + device.setup_smu(params['MAP']['TG'],params["SMU_T"]) + device.setup_smu(params['MAP']['D'],params["SMU_D"]) + device.setup_smu(params['MAP']['BG'],params["SMU_B"]) + device.setup_smu(params['MAP']['S'],params["SMU_S"]) + + device.setup_var1(params["VAR1"]) + device.setup_var2(params["VAR2"]) + + device.integration_time(params["INTEGRATION"]) + + variables_list = ['VDS','ID','VTG','ITG'] + device.variables_to_save(variables_list) + + try: + plotted_variables = graph_tool(params,device) + except Exception as e: + error_box(e) + return + + device.single_measurement() + + while device.operation_completed()==False: + pass + + device.autoscaling() + device.error_occured() + + 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 + + + # plot results + # calculate normalization factor + norm = normalization_factor(params['SAMPLE']["width"]) + points = params["VAR2"]["points"] + + # 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"{params['SAMPLE']['sample']}_{params['SAMPLE']['field']}_{params['SAMPLE']['device']}_TOP_GATE_A.txt" + + 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 =default_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() + + with open(file,'w') as f: + date = str(datetime.today().replace(microsecond=0)) + f.write(f"Output Curve at {date}"+"\n") + f.write(f"Series:{params['SAMPLE']['processing_number']}"+"\n") + f.write(f"Sample:{params['SAMPLE']['sample']}"+"\n") + f.write(f"Field:{params['SAMPLE']['field']}"+"\n") + f.write(f"Device:{params['SAMPLE']['device']}"+"\n") + f.write(f"Device Width/um:{params['SAMPLE']['width']}"+"\n") + f.write("Sweeping Gate:VTG"+"\n\n") + + f.write('Parameters\n') + f.write(f"VTG from {params['VAR2']['start']}V to {params['VAR2']['stop']}V with step {params['VAR2']['step']}V"+"\n") + f.write(f"VDS from {params['VAR1']['start']}V to {params['VAR1']['stop']}V with step {params['VAR1']['step']}V"+"\n") + + #calculate the values + if params['VAR2']['pcomp']==0: + f.write(f"Top Gate Current Compliance/A:{params['VAR2']['comp']}"+"\n") + else: + f.write(f"Top Gate Power Compliance/A:{params['VAR2']['pcomp']}"+"\n") + + if params['VAR1']['pcomp'] == 0: + f.write(f"Drain Current Compliance/A:{params['VAR1']['comp']}"+"\n") + else: + f.write(f"Drain Power Compliance/A:{params['VAR1']['pcomp']}"+"\n") + + f.write(f"Integration Time:{params['INTEGRATION']}"+"\n") + + f.write("\nResults\n") + + df.to_csv(file,sep=" ",mode='a') + + plot_values = values_to_plot(params,device) + + # Plot user specified results + fig,ax1= plt.subplots(figsize=(10,6),layout='constrained') + + plot_list = [params["PLOT"]["x"],params["PLOT"]["y1"],params["PLOT"]["y2"]] + scale_list =['LIN',params["PLOT"]["y1_scale"],params["PLOT"]["y2_scale"]] + axes_labels = set_axes_labels(plot_list) + + if scale_list[1]=='LOG': + ax1.set_yscale('log') + + #now set the labels + ax1.set_xlabel(axes_labels[0]) + ax1.set_ylabel(axes_labels[1]) + + x = np.split(plot_values['X'],points) + y1 = np.split(plot_values['Y1'],points) + labels =np.mean(np.array_split(df["VTG/V"],points),axis = 1) # VDS values for labels + + for i in range(points): + ax1.plot(x[i],y1[i],label = f"VTG:{round(labels[i],3)} (V)") + + # Adding title + fig.suptitle('Output Curve', fontweight ="bold") + fig.legend(loc='outside right upper') + + display(fig) + #save plot if checked + if params["SAMPLE"]['save_fig'] == True: + filename= os.path.splitext(file)[0] + fig.savefig(filename+'_Y1.png') + + #add the second axis if applicable + if plot_list[2]!= "None": + fig,ax2=plt.subplots(figsize=(10,6),layout='constrained') + y2 = np.split(plot_values['Y2'],points) + + if scale_list[2]=='LOG': + ax2.set_yscale('log') + + ax2.set_xlabel(axes_labels[0]) + + ax2.set_ylabel(axes_labels[2]) + for i in range(points): + ax2.plot(x[i],y2[i],label = f"VTG:{round(labels[i],3)} (V)") + + # Adding title + fig.suptitle('Output Curve', fontweight ="bold") + fig.legend(loc='outside right upper') + + display(fig) + #save plot if checked + if params["SAMPLE"]['save_fig'] == True: + filename= os.path.splitext(file)[0] + fig.savefig(filename+'Y2.png') + + +#Output VBG +def Output_VBG(device,params): + try: + device.setup_smu(params["MAP"]['TG'],params["SMU_T"]) + device.setup_smu(params["MAP"]['D'],params["SMU_D"]) + device.setup_smu(params["MAP"]['BG'],params["SMU_B"]) + device.setup_smu(params["MAP"]['S'],params["SMU_S"]) + + + device.setup_var1(params["VAR1"]) + device.setup_var2(params["VAR2"]) + + device.integration_time(params["INTEGRATION"]) + + variables_list = ['VDS','ID','VBG','IBG'] + device.variables_to_save(variables_list) + + try: + plotted_variables = graph_tool(params,device) + except Exception as e: + error_box(e) + return + + + device.single_measurement() + + while device.operation_completed()==False: + pass + + device.autoscaling() + device.error_occured() + + 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 + + # plot results + + # calculate normalization factor + norm = normalization_factor(params["SAMPLE"]["width"]) + points = params["VAR2"]["points"] + + # 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"{params['SAMPLE']['sample']}_{params['SAMPLE']['field']}_{params['SAMPLE']['device']}_BACK_GATE_A.txt" + + 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 =default_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() + + with open(file,'w') as f: + date = str(datetime.today().replace(microsecond=0)) + f.write(f"Output Curve at {date}"+"\n") + f.write(f"Series:{params['SAMPLE']['processing_number']}"+"\n") + f.write(f"Sample:{params['SAMPLE']['sample']}"+"\n") + f.write(f"Field:{params['SAMPLE']['field']}"+"\n") + f.write(f"Device:{params['SAMPLE']['device']}"+"\n") + f.write(f"Device Width/um:{params['SAMPLE']['width']}"+"\n") + f.write("Sweeping Gate:VBG"+"\n\n") + + f.write('Parameters\n') + f.write(f"VBG from {params['VAR2']['start']}V to {params['VAR2']['stop']}V with step {params['VAR2']['step']}V"+"\n") + f.write(f"VDS from {params['VAR1']['start']}V to {params['VAR1']['stop']}V with step {params['VAR1']['step']}V"+"\n") + + #calculate the values + if params['VAR2']['pcomp']==0: + f.write(f"Back Gate Current Compliance/A:{params['VAR2']['comp']}"+"\n") + else: + f.write(f"Back Gate Power Compliance/A:{params['VAR2']['pcomp']}"+"\n") + + if params['VAR1']['pcomp'] == 0: + f.write(f"Drain Current Compliance/A:{params['VAR1']['comp']}"+"\n") + else: + f.write(f"Drain Power Compliance/A:{params['VAR1']['pcomp']}"+"\n") + + f.write(f"Integration Time:{params['INTEGRATION']}"+"\n") + + f.write("\nResults\n") + + + plot_values = values_to_plot(params,device) + + # Plot user specified results + fig,ax1= plt.subplots(figsize=(10,6),layout='constrained') + + plot_list = [params["PLOT"]["x"],params["PLOT"]["y1"],params["PLOT"]["y2"]] + scale_list =['LIN',params["PLOT"]["y1_scale"],params["PLOT"]["y2_scale"]] + axes_labels = set_axes_labels(plot_list) + + if scale_list[1]=='LOG': + ax1.set_yscale('log') + + #now set the labels + ax1.set_xlabel(axes_labels[0]) + ax1.set_ylabel(axes_labels[1]) + + x = np.split(plot_values['X'],points) + y1 = np.split(plot_values['Y1'],points) + labels =np.mean(np.array_split(df["VBG/V"],points),axis = 1) # VDS values for labels + + for i in range(points): + ax1.plot(x[i],y1[i],label = f"VBG:{round(labels[i],3)} (V)") + + # Adding title + fig.suptitle('Output Curve', fontweight ="bold") + fig.legend(loc='outside right upper') + + display(fig) + #save plot if checked + if params["SAMPLE"]['save_fig'] == True: + filename= os.path.splitext(file)[0] + fig.savefig(filename+'_Y1.png') + + #add the second axis if applicable + if plot_list[2]!= "None": + fig,ax2=plt.subplots(figsize=(10,6),layout='constrained') + y2 = np.split(plot_values['Y2'],points) + + if scale_list[2]=='LOG': + ax2.set_yscale('log') + + ax2.set_xlabel(axes_labels[0]) + + ax2.set_ylabel(axes_labels[2]) + for i in range(points): + ax2.plot(x[i],y2[i],label = f"VBG:{round(labels[i],3)} (V)") + + # Adding title + fig.suptitle('Output Curve', fontweight ="bold") + fig.legend(loc='outside right upper') + + display(fig) + #save plot if checked + if params["SAMPLE"]['save_fig'] == True: + filename= os.path.splitext(file)[0] + fig.savefig(filename+'Y2.png') + +# Output both +def Output_BOTH(device,params): + try: + device.setup_smu(params["MAP"]['TG'],params["SMU_T"]) + device.setup_smu(params["MAP"]['D'],params["SMU_D"]) + device.setup_smu(params["MAP"]['BG'],params["SMU_B"]) + device.setup_smu(params["MAP"]['S'],params["SMU_S"]) + + + device.setup_var1(params["VAR1"]) + device.setup_var2(params["VAR2"]) + + + device.integration_time(params["INTEGRATION"]) + try: + plotted_variables = graph_tool(params,device) + except Exception as e: + error_box(e) + return + + + + variables_list = ['VDS','ID','VBG','IBG','VTG','ITG'] + device.variables_to_save(variables_list) + + for i , value in enumerate(params["VAR3"]['values']): + cons = device.cons_smu_dict() + cons.update(comp = params['VAR3']['comp'],value = value) + device.setup_cons_smu(params['MAP']['BG'],cons) + + if i == 0: + device.single_measurement() + else: + device.append_measurement() + + while device.operation_completed()==False: + pass + device.autoscaling() + device.error_occured() + + 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 + + # plot results + points = params["VAR2"]['points']*params["VAR3"]["points"] # number of curves + + # calculate normalization factor + norm = normalization_factor(params["SAMPLE"]["width"]) + + # 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"{params['SAMPLE']['sample']}_{params['SAMPLE']['field']}_{params['SAMPLE']['device']}_BOTH_GATES_A.txt" + + 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 =default_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() + + with open(file,'w') as f: + date = str(datetime.today().replace(microsecond=0)) + f.write(f"Output Curve at {date}"+"\n") + f.write(f"Series:{params['SAMPLE']['processing_number']}"+"\n") + f.write(f"Sample:{params['SAMPLE']['sample']}"+"\n") + f.write(f"Field:{params['SAMPLE']['field']}"+"\n") + f.write(f"Device:{params['SAMPLE']['device']}"+"\n") + f.write(f"Device Width/um:{params['SAMPLE']['width']}"+"\n") + f.write("Sweeping Gates:VBG,VTG"+"\n\n") + + f.write('Parameters\n') + f.write(f"VBG from {params['VAR3']['start']}V to {params['VAR3']['stop']}V with step {params['VAR3']['step']}V"+"\n") + f.write(f"VTG from {params['VAR2']['start']}V to {params['VAR2']['stop']}V with step {params['VAR2']['step']}V"+"\n") + f.write(f"VDS from {params['VAR1']['start']}V to {params['VAR1']['stop']}V with step {params['VAR1']['step']}V"+"\n") + + + #calculate the values + f.write(f"Back Gate Current Compliance/A:{params['VAR3']['comp']}"+"\n") + + if params['VAR2']['pcomp']==0: + f.write(f"Top Gate Current Compliance/A:{params['VAR2']['comp']}"+"\n") + else: + f.write(f"Top Gate Power Compliance/A:{params['VAR2']['pcomp']}"+"\n") + + + if params['VAR1']['pcomp'] == 0: + f.write(f"Drain Current Compliance/A:{params['VAR1']['comp']}"+"\n") + else: + f.write(f"Drain Power Compliance/A:{params['VAR1']['pcomp']}"+"\n") + + f.write(f"Integration Time:{params['INTEGRATION']}"+"\n") + + f.write("\nResults\n") + + df.to_csv(file,sep=" ",mode='a') + + plot_values = values_to_plot(params,device) + + # Plot user specified results + fig,ax1= plt.subplots(figsize=(10,6),layout='constrained') + + plot_list = [params["PLOT"]["x"],params["PLOT"]["y1"],params["PLOT"]["y2"]] + scale_list =['LIN',params["PLOT"]["y1_scale"],params["PLOT"]["y2_scale"]] + axes_labels = set_axes_labels(plot_list) + + if scale_list[1]=='LOG': + ax1.set_yscale('log') + + #now set the labels + ax1.set_xlabel(axes_labels[0]) + ax1.set_ylabel(axes_labels[1]) + + x = np.split(plot_values['X'],points) + y1 = np.split(plot_values['Y1'],points) + labels_VTG =np.mean(np.array_split(df["VTG/V"],points),axis = 1) # VTG values for labels + labels_VBG = np.mean(np.array_split(df["VBG/V"],points),axis = 1) # VBG values for labels + + for i in range(points): + ax1.plot(x[i],y1[i],label = f"VTG:{round(labels_VTG[i],3)} (V) , VBG:{round(labels_VBG[i],3)} (V)") + + # Adding title + fig.suptitle('Output Curve', fontweight ="bold") + fig.legend(loc='outside right upper') + + display(fig) + #save plot if checked + if params["SAMPLE"]['save_fig'] == True: + filename= os.path.splitext(file)[0] + fig.savefig(filename+'_Y1.png') + + #add the second axis if applicable + if plot_list[2]!= "None": + fig,ax2=plt.subplots(figsize=(10,6),layout='constrained') + y2 = np.split(plot_values['Y2'],points) + + if scale_list[2]=='LOG': + ax2.set_yscale('log') + + ax2.set_xlabel(axes_labels[0]) + + ax2.set_ylabel(axes_labels[2]) + for i in range(points): + ax2.plot(x[i],y2[i],label = f"VTG:{round(labels_VTG[i],3)} (V) , VBG:{round(labels_VBG[i],3)} (V)") + + # Adding title + fig.suptitle('Output Curve', fontweight ="bold") + fig.legend(loc='outside right upper') + + display(fig) + #save plot if checked + if params["SAMPLE"]['save_fig'] == True: + filename= os.path.splitext(file)[0] + fig.savefig(filename+'_Y2.png') + + +def Gatediode_VTG(device,params): + try: + device.setup_smu(params["MAP"]['TG'],params["SMU_T"]) + device.smu_disable(params["MAP"]['BG']) + device.setup_smu(params["MAP"]['S'],params["SMU_S"]) + + device.setup_var1(params["VAR1"]) + + device.integration_time(params["INTEGRATION"]) + + variables_list = ['VTG','ITG'] + device.variables_to_save(variables_list) + try: + plotted_variables = graph_tool(params,device) + except Exception as e: + error_box(e) + return + + + device.single_measurement() + + while device.operation_completed()==False: + pass + device.autoscaling() + device.error_occured() + + 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 + + # calculate normalization factor + norm = normalization_factor(params['SAMPLE']["width"]) + + # Append the normalized current + df["ITGmm/uA/um"]= (df["ITG/A"].apply(lambda x: Decimal(str(x))*Decimal(str(norm)))).astype('float') + + # Save the results + default_filename = f"{params['SAMPLE']['sample']}_{params['SAMPLE']['field']}_{params['SAMPLE']['device']}_TOP_GATE_D.txt" + + 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 =default_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() + + with open(file,'w') as f: + date = str(datetime.today().replace(microsecond=0)) + f.write(f"Gatediode Curve at {date}"+"\n") + f.write(f"Series:{params['SAMPLE']['processing_number']}"+"\n") + f.write(f"Sample:{params['SAMPLE']['sample']}"+"\n") + f.write(f"Field:{params['SAMPLE']['field']}"+"\n") + f.write(f"Device:{params['SAMPLE']['device']}"+"\n") + f.write(f"Device Width/um:{params['SAMPLE']['width']}"+"\n") + f.write("Sweeping Gate:VTG"+"\n\n") + + f.write('Parameters\n') + f.write(f"VTG from {params['VAR1']['start']}V to {params['VAR1']['stop']}V with step {params['VAR1']['step']}V"+"\n") + + #calculate the values + if params['VAR1']['pcomp']==0: + f.write(f"Top Gate Current Compliance/A:{params['VAR1']['comp']}"+"\n") + else: + f.write(f"Top Gate Power Compliance/A:{params['VAR1']['pcomp'].value}"+"\n") + + f.write(f"Integration Time:{params['INTEGRATION']}"+"\n") + + f.write("\nResults\n") + + df.to_csv(file,sep=" ",mode='a') + + plot_values = values_to_plot(params,device) + # Plot user specified results + fig,ax1= plt.subplots(figsize=(10,6),layout='constrained') + + plot_list = [params["PLOT"]["x"],params["PLOT"]["y1"],params["PLOT"]["y2"]] + scale_list =['LIN',params["PLOT"]["y1_scale"],params["PLOT"]["y2_scale"]] + axes_labels =set_axes_labels(plot_list) + + if scale_list[1]=='LOG': + ax1.set_yscale('log') + + + #now set the labels + ax1.set_xlabel(axes_labels[0]) + ax1.set_ylabel(axes_labels[1]) + + x = plot_values['X'] + y1 = plot_values['Y1'] + + ax1.plot(x,y1) + fig.suptitle('Gatediode Curve', fontweight ="bold") + display(fig) + + if params["SAMPLE"]["save_fig"] == True: + filename= os.path.splitext(file)[0] + fig.savefig(filename+'_Y1.png') + + + #add the second axis if applicable + if plot_list[2]!= "None": + fig,ax2=plt.subplots(figsize=(10,6),layout='constrained') + y2 = plot_values['Y2'] + + if scale_list[2]=='LOG': + ax2.set_yscale('log') + + ax2.set_xlabel(axes_labels[0]) + ax2.set_ylabel(axes_labels[2]) + ax2.plot(x,y2) + + fig.suptitle('Gatediode Curve', fontweight ="bold") + display(fig) + + if params["SAMPLE"]["save_fig"] == True: + filename= os.path.splitext(file)[0] + fig.savefig(filename+'_Y2.png') + + +def Gatediode_VBG(device,params): + try: + device.setup_smu(params["MAP"]['BG'],params["SMU_B"]) + device.smu_disable(params["MAP"]['TG']) + device.setup_smu(params["MAP"]['S'],params["SMU_S"]) + + device.setup_var1(params["VAR1"]) + + device.integration_time(params["INTEGRATION"]) + + variables_list = ['VBG','IBG'] + device.variables_to_save(variables_list) + + try: + plotted_variables = graph_tool(params,device) + except Exception as e: + error_box(e) + return + + + device.single_measurement() + while device.operation_completed()==False: + pass + + device.autoscaling() + device.error_occured() + + 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 + + # plot results + + # calculate normalization factor + norm = normalization_factor(params["SAMPLE"]["width"]) + + # Append the normalized current + df["IBGmm/uA/um"]= (df["IBG/A"].apply(lambda x: Decimal(str(x))*Decimal(str(norm)))).astype('float') + + # Save the results + default_filename = f"{params['SAMPLE']['sample']}_{params['SAMPLE']['field']}_{params['SAMPLE']['device']}_BACK_GATE_D.txt" + + 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 =default_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() + + with open(file,'w') as f: + date = str(datetime.today().replace(microsecond=0)) + f.write(f"Gatediode Curve at {date}"+"\n") + f.write(f"Series:{params['SAMPLE']['processing_number']}"+"\n") + f.write(f"Sample:{params['SAMPLE']['sample']}"+"\n") + f.write(f"Field:{params['SAMPLE']['field']}"+"\n") + f.write(f"Device:{params['SAMPLE']['device']}"+"\n") + f.write(f"Device Width/um:{params['SAMPLE']['width']}"+"\n") + f.write("Sweeping Gate:VBG"+"\n\n") + + f.write('Parameters\n') + f.write(f"VBG from {params['VAR1']['start']}V to {params['VAR1']['stop']}V with step {params['VAR1']['step']}V"+"\n") + + #calculate the values + if params['VAR1']['pcomp']==0: + f.write(f"Back Gate Current Compliance/A:{params['VAR1']['comp']}"+"\n") + else: + f.write(f"Back Gate Power Compliance/A:{params['VAR1']['pcomp']}"+"\n") + + f.write(f"Integration Time:{params['INTEGRATION']}"+"\n") + + f.write("\nResults\n") + + df.to_csv(file,sep=" ",mode='a') + + plot_values = values_to_plot(params,device) + # Plot user specified results + fig,ax1= plt.subplots(figsize=(10,6),layout='constrained') + + plot_list = [params["PLOT"]["x"],params["PLOT"]["y1"],params["PLOT"]["y2"]] + scale_list =['LIN',params["PLOT"]["y1_scale"],params["PLOT"]["y2_scale"]] + axes_labels =set_axes_labels(plot_list) + + if scale_list[1]=='LOG': + ax1.set_yscale('log') + + + #now set the labels + ax1.set_xlabel(axes_labels[0]) + ax1.set_ylabel(axes_labels[1]) + + x = plot_values['X'] + y1 = plot_values['Y1'] + + ax1.plot(x,y1) + fig.suptitle('Gatediode Curve', fontweight ="bold") + display(fig) + + if params["SAMPLE"]["save_fig"] == True: + filename= os.path.splitext(file)[0] + fig.savefig(filename+'_Y1.png') + + + #add the second axis if applicable + if plot_list[2]!= "None": + fig,ax2=plt.subplots(figsize=(10,6),layout='constrained') + y2 = plot_values['Y2'] + + if scale_list[2]=='LOG': + ax2.set_yscale('log') + + ax2.set_xlabel(axes_labels[0]) + ax2.set_ylabel(axes_labels[2]) + ax2.plot(x,y2) + + fig.suptitle('Gatediode Curve', fontweight ="bold") + display(fig) + + if params["SAMPLE"]["save_fig"] == True: + filename= os.path.splitext(file)[0] + fig.savefig(filename+'_Y2.png') \ No newline at end of file diff --git a/hp4155/hp4155a.py b/hp4155/hp4155a.py index b48a8e0a3629033bed373d2e7c455c41e14ef8ca..e6d32d16809b195bf4f3351501e7a277ed8239d1 100644 --- a/hp4155/hp4155a.py +++ b/hp4155/hp4155a.py @@ -43,9 +43,22 @@ class HP4155a(object): #go to stress page def stress_page(self): self.inst.write(":PAGE:STR") - + def error(self): - return self.inst.query(":SYST:ERR?") + error = self.inst.query(":SYST:ERR?") + error_code = int(error.split(',',1)[0]) + error_message = error.split(',',1)[1] + return error_code,error_message + + def error_occured(self): + code,message = self.error() + if code != 0: + raise Exception(message) + + def clear_error_stack(self): + code,_ = self.error() + while code != 0: + code,_ = self.error() def operation_completed(self): text = self.inst.query('*OPC?') @@ -161,6 +174,11 @@ class HP4155a(object): def display_variable_min_max(self,axis,extremum,value): command = f":PAGE:DISP:GRAP:{axis}:{extremum} {value}" self.inst.write(command) + + def get_axis_variable(self,axis): + command = f":PAGE:DISP:GRAP:{axis}:NAME?" + var_name = self.inst.query(command) + return var_name.rstrip() #remove \n def autoscaling(self): self.inst.write(":PAGE:GLIS:SCAL:AUTO ONCE") @@ -186,6 +204,10 @@ class HP4155a(object): command = f":PAGE:DISP:MODE {mode}" self.inst.write(command) + def delete_axis(self,axis): + command = f":PAGE:DISP:GRAP:{axis}:DEL" + self.inst.write(command) + #delete all user functions def del_user_functions(self):