diff --git a/hp4155/pn_Diode/diode.py b/hp4155/pn_Diode/diode.py new file mode 100644 index 0000000000000000000000000000000000000000..3e0bb3801d52ce943381f950be76710e36150d0b --- /dev/null +++ b/hp4155/pn_Diode/diode.py @@ -0,0 +1,42 @@ +import sys +sys.path.insert(0, './lib') +sys.path.insert(0, '..') #append parent directory + +import hp4155a +from interface import * +from help import * + +sample = sample_information_interface() +parameters_1 = parameters_interface(0,0.02,10,"First Measurement") +parameters_2 = parameters_interface(0,-0.5,-100,"Second Measurement") +button = widgets.Button(description ='Start Measurement') +output = widgets.Output() + +display(button,output) +all_widgets = [button] +add_widgets_to_list(sample,all_widgets) +add_widgets_to_list(parameters_1,all_widgets) +add_widgets_to_list(parameters_2,all_widgets) + + +device = hp4155a.HP4155a('GPIB0::17::INSTR') +setup(device) +def on_button_clicked(b): + with output: + clear_output(wait = True) + change_state(all_widgets) + area=circle_area(sample['radius'].value) + + valid = check_values(parameters_1) and check_values(parameters_2) + if valid == True: + df = measure(parameters_1,parameters_2,area,device) + plot_results(df['VS'],df['ABSNOR']) + filename = f"pn_diode_{sample['field'].value}.txt" + file = create_file(filename) + save_to_file(df,sample,parameters_1,parameters_2,file) + + change_state(all_widgets) + + + +button.on_click(on_button_clicked) \ No newline at end of file diff --git a/hp4155/pn_Diode/diode_interface.ipynb b/hp4155/pn_Diode/diode_interface.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..68a389fba486d5c13ec925efac7af9f2d4ce2ea9 --- /dev/null +++ b/hp4155/pn_Diode/diode_interface.ipynb @@ -0,0 +1,114 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "1b607685-438c-466e-9671-140ae6c8d9f9", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "0560c5f898da48b5a9865867f6d759be", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(VBox(children=(Label(value='Sample Information', layout=Layout(height='auto', width='auto'), st…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "5e698a443fc74b1c8e6abb0aa87200c3", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "GridspecLayout(children=(Label(value='First Measurement', layout=Layout(grid_area='widget001', height='auto', …" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "1ec1a195abd94a8191553ab782e2d2af", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "GridspecLayout(children=(Label(value='Second Measurement', layout=Layout(grid_area='widget001', height='auto',…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "1692c4ee58ed4f658f5b38cd2b4035d4", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Button(description='Start Measurement', style=ButtonStyle())" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "72063b77dec54b4980a4a8d946f78b98", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Output()" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%run diode.py" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5bc60dda-f423-4cdc-bc39-3404a8cfa1df", + "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/pn_Diode/lib/diode_smu_configuration.png b/hp4155/pn_Diode/lib/diode_smu_configuration.png new file mode 100644 index 0000000000000000000000000000000000000000..86131ea625c4f279226f3dcb838941e1561d02ba Binary files /dev/null and b/hp4155/pn_Diode/lib/diode_smu_configuration.png differ diff --git a/hp4155/pn_Diode/lib/help.py b/hp4155/pn_Diode/lib/help.py new file mode 100644 index 0000000000000000000000000000000000000000..7a669abeaf4f09d7d9948456fc8dc97ac7bab7c8 --- /dev/null +++ b/hp4155/pn_Diode/lib/help.py @@ -0,0 +1,248 @@ +import matplotlib.pyplot as plt +import matplotlib +matplotlib.use('TkAgg',force = True) + +import numpy as np +import time +from datetime import datetime + +import tkinter as tk +from tkinter import filedialog +import tkinter.messagebox + +import pandas as pd + +#returns area in cm^2 +def circle_area(radius): + #input is in um + radius = radius*10**(-6) #m^2 + area = np.pi*radius**2 # m^2 + area = area * 10**4 # cm^2 + return area + +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 + +#saving functions : save parameters and dataframe to txt +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 + answer = tk.messagebox.askyesno(message = "Do you want to cancel the file operation?") + if answer == True: + file = None + break + file = filedialog.asksaveasfilename(defaultextension=".txt", filetypes=[("Text files","*.txt")],title = "save results path",initialfile =filename) + root.destroy() + return file + + +def check_values(parameters): + 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 + + #get better variable names + start = parameters['start'] + stop = parameters['stop'] + step = parameters['step'] + + #check values + if abs(step.value) > abs(stop.value-start.value) or step.value==0:#invalid parameter setting + valid = False + tkinter.messagebox.showerror(message="Invalid parameter setting!") + + if start.value<stop.value and step.value<0: #change polarity + step.value =(-1)*step.value + + elif start.value>stop.value and step.value>0: + step.value = (-1)*step.value + + else: + pass + + root.destroy() + return valid + +#Setup The device for diode measurement +def setup(device): + device.reset() + + #setup sweep measurement mode + device.measurement_mode('SWE') + + #disable all irrelevant units + device.disable_not_smu() + + #disable smu 2 and 4 + device.smu_disable(2) + device.smu_disable(4) + + smu1 = device.smu_dict() + smu1.update(vname ='VGND',iname = 'IGND',mode = 'COMM',func='CONS') + + smu3 = device.smu_dict() + smu3.update(vname='VS',iname='IS',mode ='V',func='VAR1') + + + device.setup_smu(1,smu1) + device.setup_smu(3,smu3) + + +def measure(parameters_1,parameters_2,area,device): + #del all user functions + device.del_user_functions() + + #define user functions + device.user_function("AREA",'CM^2',str(area)) + device.user_function("INORM",'A/CM^2','IS/AREA') + device.user_function("ABSNOR","A/CM^2","ABS(INORM)") + device.user_function("ABSRAW",'A','ABS(IS)') + + var1 = device.var1_dict() + var1.update( + mode = parameters_1['hyst'].value, + start = parameters_1['start'].value, + stop = parameters_1['stop'].value, + step = parameters_1['step'].value, + comp = parameters_1['comp'].value, + pcomp = 0 + ) + + device.setup_var1(var1) + + device.integration_time(parameters_1['integration'].value) + + + #display + device.display_variable('X','VS') + + device.display_variable('Y1','ABSNOR') + + + device.axis_scale('Y1','LOG') + + if parameters_1['start'].value < parameters_1['stop'].value: + device.display_variable_min_max('X','MIN',parameters_1['start'].value) + device.display_variable_min_max('X','MAX',parameters_1['stop'].value) + + else: + device.display_variable_min_max('X','MAX',parameters_1['start'].value) + device.display_variable_min_max('X','MIN',parameters_1['stop'].value) + + if parameters_1['comp'].value !=0: + device.display_variable_min_max('Y1','MIN',0) + device.display_variable_min_max('Y1','MAX',abs(parameters_1['comp'].value)/area) + + + variables_list = ['VS','IS','ABSRAW','INORM','ABSNOR']# look at the tool + device.variables_to_save(variables_list) + + + device.single_measurement() + while device.operation_completed() == False: + pass + + device.autoscaling() + + # Start Second measurement + var1.update( + mode = parameters_2['hyst'].value, + start = parameters_2['start'].value, + stop = parameters_2['stop'].value, + step = parameters_2['step'].value, + comp = parameters_2['comp'].value, + pcomp = 0 + ) + + device.setup_var1(var1) + device.integration_time(parameters_2['integration'].value) + + if parameters_2['start'].value < parameters_2['stop'].value: + device.display_variable_min_max('X','MIN',parameters_2['start'].value) + device.display_variable_min_max('X','MAX',parameters_2['stop'].value) + + else: + device.display_variable_min_max('X','MAX',parameters_2['start'].value) + device.display_variable_min_max('X','MIN',parameters_2['stop'].value) + + if parameters_2['comp'].value !=0: + device.display_variable_min_max('Y1','MIN',0) + device.display_variable_min_max('Y1','MAX',abs(parameters_2['comp'].value)/area) + + device.append_measurement() + while device.operation_completed() == False: + pass + device.autoscaling() + + values = dict([(variable,device.return_values(variable)) for variable in variables_list]) + df = pd.DataFrame(values) + df = df.sort_values(by='VS') + return df + +def plot_results(x,y): + fig, ax1 = plt.subplots() + + color = 'tab:red' + ax1.set_xlabel('Voltage (V)') + ax1.set_ylabel('Current density $(A/{cm}^2)$') + ax1.set_yscale('log') + + ax1.plot(x,y, color = color) + + ax1.grid(True,linestyle = '--',axis = 'y',color ="k",linewidth = 0.5) + ax1.axvline(linestyle='--',color = 'k',linewidth =0.5) + + fig.suptitle('Diode Plot', fontweight ="bold") + fig.tight_layout() + + wm = plt.get_current_fig_manager() + wm.window.state('zoomed') + plt.show(block = True) + + +#also for file +def save_to_file(df,sample,parameters_1,parameters_2,file): + if file != None: + with open(file,'w') as f: + date = str(datetime.today().replace(microsecond=0)) + f.write(f"Diode Measurement at {date}"+"\n") + f.write(f"Series:{sample['processing_number'].value}"+"\n") + f.write(f"Sample:{sample['sample'].value}"+"\n") + f.write(f"Field:{sample['field'].value}"+"\n") + f.write(f"Radius/um:{sample['radius'].value}"+"\n\n") + + f.write('Parameters of first Measurement\n') + f.write(f"V from {parameters_1['start'].value}V to {parameters_1['stop'].value}V with step {parameters_1['step'].value}V"+"\n") + f.write(f"I Compliance/A:{parameters_1['comp'].value}"+"\n") + f.write(f"Integration Time:{parameters_1['integration'].value}"+"\n") + + f.write('\nParameters of second Measurement\n') + f.write(f"V from {parameters_2['start'].value}V to {parameters_2['stop'].value}V with step {parameters_2['step'].value}V"+"\n") + f.write(f"I Compliance/A:{parameters_2['comp'].value}"+"\n") + f.write(f"Integration Time:{parameters_2['integration'].value}"+"\n") + + f.write("\nResults\n") + + #create pandas dataframe + #df =df.drop(columns=['ABSNOR']) + df =df.rename(columns={'VS':'VS/V','IS':'IRAW/A','ABSRAW':'ABSRAW/A','INORM':'INORM/A/CM^2','ABSNOR':'ABSNOR/A/CM^2'}) + + df.to_csv(file,sep= " ",mode = 'a') \ No newline at end of file diff --git a/hp4155/pn_Diode/lib/interface.py b/hp4155/pn_Diode/lib/interface.py new file mode 100644 index 0000000000000000000000000000000000000000..2253b9edb5b4282fc419329217c5ea0c8187fdf0 --- /dev/null +++ b/hp4155/pn_Diode/lib/interface.py @@ -0,0 +1,111 @@ +import ipywidgets as widgets +from ipywidgets import GridspecLayout,Layout +from IPython.display import clear_output,display +import sys +import os + + +style = {'description_width': 'initial'} + +def parameters_interface(start,step,stop,title): + grid = GridspecLayout(5,4) + grid[0,:]=widgets.Label(title,layout=Layout(height='auto', width='auto')) + grid[0,:].style.font_weight='bold' + + + #first line + grid[1,0]=widgets.Label("Start(V)",layout=Layout(height='auto', width='auto')) + grid[1,1]=widgets.Label("Step(V)",layout=Layout(height='auto', width='auto')) + grid[1,2]=widgets.Label("Stop(V)",layout=Layout(height='auto', width='auto')) + + #second line + grid[2,0]=widgets.BoundedFloatText(value=start,min=-100,max=100,step=1,layout=Layout(height='auto', width='auto')) + grid[2,1]=widgets.BoundedFloatText(value=step,min=-200,max=200,step=1,layout=Layout(height='auto', width='auto')) + grid[2,2]=widgets.BoundedFloatText(value=stop,min=-100,max=100,step=1,layout=Layout(height='auto', width='auto')) + + #third line + grid[3,0]=widgets.Label("Compliance(A)",layout=Layout(height='auto', width='auto')) + grid[3,1] =widgets.Label("Integration Time",layout=Layout(height='auto', width='auto'))#mind the gap + grid[3,2] =widgets.Label("Hysterisis",layout=Layout(height='auto', width='auto'))#mind the gap + + #fourth line + grid[4,0]=widgets.BoundedFloatText(value=0.01,min=-0.1,max=0.1,step=0.01,layout=Layout(height='auto', width='auto')) + grid[4,1]=widgets.Dropdown(options=["SHORt","MEDium","LONG"],value="MEDium",style =style,layout=Layout(height='auto', width='auto')) + grid[4,2]=widgets.Dropdown(options=['SINGle','DOUBle'],value='SINGle',layout=Layout(height='auto', width='auto'))#mind the gap + + parameters = { + 'start': grid[2,0], + 'step': grid[2,1], + 'stop': grid[2,2], + 'comp': grid[4,0], + 'hyst':grid[4,2], + 'integration':grid[4,1] + } + + + display(grid) + return parameters + + +def sample_information_interface(): + sample_information=widgets.Label("Sample Information",layout=Layout(height='auto', width='auto')) + 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: + pass + elif i == 2 and j == 0: #look at the tool for diode area + information_grid[i,j]=widgets.BoundedFloatText( + value=50, + min=1, + max=200,step=1, + layout=Layout(height='auto', width='auto') + ) + else: + information_grid[i,j]=widgets.Text(layout=Layout(height='auto', width='auto')) + + information_grid[0,0].description = "Processing-Nr:" + information_grid[1,0].description = "Sample:" + information_grid[2,0].description = "Radius(um):" + information_grid[0,1].description = "Field(XYY):" + information_grid[1,1].description = "Device:" + information_grid[1,1].disabled = True + + + for i in range(3): + for j in range(2): + try: + information_grid[i,j].style = style + except: + pass + + image_title = widgets.Label("SMU Configuration",layout=Layout(height='auto', width='auto')) + image_title.style.font_weight='bold' + + filename = os.getcwd()+r"\lib\diode_smu_configuration.png" + #print(filename) + + file = open(filename, "rb") + image = file.read() + image_widget =widgets.Image( + value = image, + format='png', + width='auto', + height='auto', + ) + + vbox1 =widgets.VBox([sample_information,information_grid]) + vbox2 =widgets.VBox([image_title,image_widget]) + + display(widgets.HBox([vbox1,vbox2])) + + information = { + 'processing_number': information_grid[0,0], + 'sample' : information_grid[1,0], + 'field': information_grid[0,1], + 'radius': information_grid[2,0], + } + + return information