IV Curve Measurement System

Order Code: T111

Price

(excluding Taxes)

£1,095.00


 

The IV Curve Measurement System provides accurate, reliable and affordable IV curve measurement and analysis of solar cells. The Xtralien X100 source meter performs current measurements from 10 nA to 100 mA, and has a voltage range of -10 V to 10 V. These metrics are well-suited to IV curve measurements of photovoltaic devices.

Perform quick and easy material characterisation using either the 6-Pixel or 8-Pixel substrate system and the Ossila Push Fit Test Board! The new swivel-lid on our push-fit test board allows for fast loading and unloading of devices.

Features

    • Source voltage from -10 V to 10 V
    • Measure up to 100 mA and down to 10 nA
    • IV curve measurements
    • Automatic device characterisation (PCE, Jsc, Voc, FF)
    • 8 devices per substrate
    • Data presented in easy-to-read format
    • Data saved to file
    • Software easily edited using our Python tutorials

 

IV Curve System Overview

The IV Curve Measurement System comprises an Xtralien X100 Source Meter, a manual test board, and custom software. 

The test board provides reliable contact with substrate electrodes via a spring-loaded push fit system. No electrical connection legs are required due to the alignment of the contact pins with the ITO patterning of the substrate.

Components schematic of the IV Curve Measurement System.
iv-curve-side-view
Side view of the complete IV Curve Measurement System.

 

Measuring an IV curve with the IV Curve Measurement System.

 

Component List

Xtralien X100

The Xtralien X100 is a source meter that provides fast and accurate measurements required for the IV Curve Measurement System.

Source meter measure specifications are displayed in the table below. There are five ranges, each with a different maximum current and accuracy. The range can be changed to suit the device under measurement. The resolution of each range is 0.1% of its maximum current. 

Range Max Current Resolution Burden Noise
1 ± 100 mA 100 µA < 10 mV 6.0 E-6
2 ± 10 mA 10 µA < 10 mV 6.0 E-7
3 ± 1 mA 1 µA < 10 mV 6.0 E-8
4 ± 100 µA 100 nA < 10 mV 6.0 E-9
5 ± 10 µA 10 nA < 10 mV 6.0 E-10

For example, a measurement of up to 10 mA at range 2 would provide an IV curve with 10 µA accuracy. Please see the Xtralien X100 Source Meter page and the Xtralien X100 Manual for more details.

Xtralien Python Software

The system is controlled using custom script written in Python. This code is freely available, can be modified for custom requirements, and can be run by downloading our Xtralien Scientific Python distribution. This software is designed to overcome the entry barrier for scientists who would like to edit the code and adapt it for their own experiments. We provide tutorials and example code to get started.

 

iv-curve-csv-data

The software takes IV curve measurements of selected device pixels and interprets the data automatically. The following metrics are calculated:

  • Power Conversion Efficiency (PCE)
  • Short Circuit Current Density (Jsc)
  • Open Circuit Voltage (Voc)
  • Fill Factor (FF)
  • Maximum Power Point (MPP)

As the software is written in Python, the user can edit and adjust it to fit in with their own experiment. 

Both the IV curve data and device metrics are saved to a .csv file for easy editing in any spreadsheet program.

 

 

from scipy import stats

####################Settings##############################

### Sweep Settings ###
smu_num = 1  #Chooose SMU number (1 or 2)
pixels = [3,4]  #Choose pixels to test
vstart = -0.2  #Starting Voltage (V)
vend = 0.8   #End Voltage (V)
vstep = 0.05 #Step Size (V)
pixel_size = 0.04  #cm^2
irrad = 100  #mW / cm^2

### X100 Settings ###
precision = 4
smu_range = 4
smu_osr = 5

### File Settings ###
save =False      #Save data? (True of False)
folder_name = 'Curve_Test' # Choose a folder name


##########################################################

vdata = [] #Create pixel data lists (pixel,voltage,current)
idata = []

### Create Variables ###
smu = 'smu' + str(smu_num)
vnum = ((vend-vstart)/vstep) + 1 #Calculate number of steps in sweep
volts = linspace(vstart,vend,vnum) #Create voltage list
base_i = 0

### Create Functions ###

def metrics(vdata_,idata_):
    ### Calculate Jsc ###
    v_min = abs(vdata_[0])
    for n,i in enumerate(idata_):
        if abs(vdata_[n]) < v_min:
            v_min = abs(vdata_[n])
            index = n
    vdatai = vdata_[index-2:index+2]
    idatai = idata_[index-2:index+2]
    slope, ishort, r_value, p_value, std_err = stats.linregress(vdatai,idatai)
    jshort = (1000 * ishort) / float(pixel_size)
    ### Calculate Voc ###
    i_min = abs(idata_[0])
    index = 2
    for n,v in enumerate(vdata_):
        if abs(idata_[n]) < i_min:
            i_min = abs(idata_[n])
            index = n
    if idata_[0] < 0:
        vdatav = vdata_[index-2:index+2]
        idatav = idata_[index-2:index+2]
    
        slope, vopen, r_value, p_value, std_err = stats.linregress(idatav,vdatav)
    else:
        vopen = 0
        print('Could not measure Voc - no I data below zero.')
        return 0
    print('v done')
    ### Calculate FF ###   
    v = array(idata_)
    i = array(vdata_)
    p = v * i
    mpp = amin(p)
    ffact = (mpp /(ishort*vopen))
    ### Calulate PCE ###
    powout = jshort*vopen*(ffact)  #Calculate the maximum power out
    powin = irrad  #Power in is irradiance
    pceff = abs((powout / powin))*100 #Calulcate the PCE and change to %
    print([vopen,jshort,ffact,pceff])
    return [vopen,jshort,ffact,pceff]

def limcheck(current,get_range):
    limit = pow(0.1,get_range)
    if abs(current) > limit:
        print('Warning: Current Over Range Limit. Set a lower range')
        print('Current: {} Range: {}'.format(current,get_range))
        return (True,current,get_range)
    else:
        return False
    
def acccheck(current,get_range):
    acc = pow(0.1,get_range)/1000
    if abs(current) < acc:
        print('Warning: Current Below Range Accuracy. Set a higher range.')
        print('Current: {} Range: {} \n'.format(current,get_range))   
        return (True,current,get_range)
    else:
        return False
### Find X100 Addresss ###
ports = serial_ports()  #Lists all COM ports
x100s = []  #Creates empty list for X100s
for COM in ports:  #For every COM port...
    with X100(COM, serial_timeout = 1) as Dev:  #Attempt connection with a COM port
        hello = Dev.cloi.hello()
        if (Dev.cloi.hello(sleep_timeout = 10)) == 'Hello World':  #If COM port returns 'Hello World...
            print(COM, 'is an X100')
            x100s.append(COM)  #Append the list of X100s connected
        else:
            print(COM,'is not an X100')

if len(x100s) == 0:  #Checks if any X100s were detected
    sys.exit('No X100s detected.')  #If there are no X100s found, the program ends


### Create Plot ###
close()
#fig = Figure()
simplefilter("ignore")  #Ignores Matplotlib warning
style.use('ggplot')
f, ax1 = subplots()
ax1.set_title('IV Curve')
xlim((vstart-vstep),(vend+vstep))
#ax1.ylim = (1e-6)
print(get_fignums)

### Perform Sweep ###
#try:
with X100.USB(x100s[0]) as Dev1:
    Dev1[smu].set.range(smu_range,response=0)
    get_range = Dev1[smu].get.range()
    Dev1[smu].set.osr(smu_osr,response=0)
    get_osr = Dev1[smu].get.osr()
    input('Turn off pixels and press Enter')  #Wait for input
    base_i = mean(Dev1[smu].measurei(10))
    for index,pix in enumerate(pixels):
        vdata.append([])
        idata.append([])
        pause(1)
        input('Set pixel {} and press Enter'.format(pix))  #Wait for input
        for v in volts:  #For every voltage in the list....
            measv,measi = Dev1[smu].oneshot(v)[0]  #Perform a oneshot measurement
            vdata[index].append(measv)  #Append the voltage data
            idata[index].append(measi-base_i)  #Append the current data
            limcheck(measi,get_range)
            cla()  #Clear the graph (this saves on memory)
            ax1.plot(vdata[index],idata[index])
            ax1.set_title('Pixel ' + str(pix))
            xlim((vstart-vstep),(vend+vstep))
            pause(0.001)  #Pause the graph (allows live plotting)
        acccheck(min(idata[index]),get_range)
        vopen,jshort,ffact,pceff = metrics(vdata[index],idata[index])
        print('Jsc is: ',jshort,' mA/cm^2')
        print('Voc is: ',vopen,' V')  #Print Voc   
        print('FF is: ',ffact*100,' %')  #Print FF
        print('PCE is: ',pceff,' %')  #Print FF
    Dev1[smu].set.voltage(0,response=0)  #Set the SMU voltage to 0

cla()  #Clear the plot
for i in range(index+1):  #For every pixel... 
    xlim((vstart-vstep),(vend+vstep))  #Set V axis to have an incremebnt gap either side
    ax1.plot(vdata[i],idata[i],label = pixels[i])  #Plot the data
legend()  #Display the legend

            
### Save to file ###
if save == True:       
    if not os.path.exists(folder_name):
        os.makedirs(folder_name)
    os.chdir(folder_name)
    for index,pix in enumerate(pixels):  #Create csv file for each pixel
        data = array([vdata[index],idata[index]]).T  #Change data into an array so that we can transpose it
        data = list(data)  #Change the data back into lists now that it is tranposed
        fieldnames = ['Voltage (V)','Current (A)']  #Create list of field names
        data.insert(0,fieldnames)  #Insert field names above the data
        date = strftime('%y%m%d_%H%M%S')
        filename = 'Pixel_' + str(pix) + '_' + date + '.csv'  #Create file names
        array_to_csv(data,filename)  #Save data to csv file