Problem 12.3#

Fundamentals of Solar Cells and Photovoltaic Systems Engineering

Solutions Manual - Chapter 12

Problem 12.3

Similar to Problem S12.1, obtain now the main parameters from the experimentally measured I-V curve under illumination at at 25 \(^{\circ}\)C provided in this book’s online repository, which corresponds to a 10 cm\(^2\) solar cell. Estimate the short-circuit current \(I_{SC}\), open-circuit voltage \(V_{OC}\), voltage and current of the maximum power point \(V_{mp}\) and \(I_{mp}\), fill factor \(FF\), and efficiency \(\eta\).

Estimate the efficiency based on a pair of data points included in the measurement and with the fitted value. What is the difference between the two estimated efficiency values?

We will follow a similar approach to that of Problem 4.11. First, we import the Python modules used and define the Boltzman constant

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

kB = 8.617333e-5

Function to extract the I-V curve parameters#

The algorithm is from:

Note that this function does not assume that the I-V curve is in the first quadrant.

Another two functions are used to calculate the Isc and Voc and detect the quadrant. Then, the function calculating Pmax moves the I-V curve to this quadrant, if it is not there yet.

# Input data:
# · experimental I-V curve
# · temperature
# · number of points around Isc to calculate the I-V slope
# · number of points around Voc to calculate the I-V slope

def get_IV_params(rawIV, temperature):
    # Thermal voltage
    kBT = 8.617333e-5*(temperature + 273.15)
    
    ## 1) Prepare the I-V curve to be in 1st quadrant and 
    ##    with increasing values of voltage
    # Move I-V curve to first quadrant if it is not there yet
    # Sort data and move to first quadrant
    IV_1stq = rawIV.copy()
    Isc = get_Isc(IV_1stq)
    if Isc<0:
        Isc*=-1
        IV_1stq[:,1]*=-1
    
    Voc = get_Voc(IV_1stq)
    if Voc<0:
        Voc*=-1
        IV_1stq[:,0]*=-1    

    # Sort I-V curve data to have increasing values of voltage
    IV_1stq=IV_1stq[IV_1stq[:,0].argsort()]     
    ##
    
    ## 2) Algorithm for the 5-point method
    ##
    
    # Calculate Rp as the inverse of the slope at V=0
    # The function "getSlope_atIsc" returns the slope and the linear function to be plotted later
    slopeIsc, slopeFitIsc = getSlope_atIsc(IV_1stq)
    Rp = -1/slopeIsc
   
     # Calculate the maximum power and V, I at maximum power point
    Pm, Vm, Im = get_Pmax(rawIV)
    
    # Calculate the slope at Voc as intermediate parameter
    # The function "getSlope_atVoc" returns the slope and the linear function to be plotted later
    slopeVoc, slopeFitVoc = getSlope_atVoc(IV_1stq)
    
    # Intermediate calculations in the 5-point algorithm
    Rs0 = -1/slopeVoc
    
    A = Vm + Rs0*Im-Voc
    B = np.log(Isc-Vm/Rp-Im)-np.log(Isc-Voc/Rp)
    C = Im/(Isc-Voc/Rp)
    n = A/(kBT*(B+C))    
    
    I0 = (Isc-Voc/Rp)*np.exp(-Voc/(n*kBT))
  
    Rs = Rs0 -n*(kBT/I0)*np.exp(-Voc/(n*kBT))
   
    IL = Isc*(1+Rs/Rp) + I0*(np.exp(Isc*Rs/(n*kBT))-1)


    return IL, I0, n, Rs, Rp, slopeFitIsc, slopeFitVoc

And ancillary functions to serve “get_IV_params”

# Obtains Isc by linear interpolation around V=0
def get_Isc(IVdata):
    """Returns the Isc of the input raw I-V curve"""

   # Sort data
    IV_sorted = IVdata.copy()
    IV_sorted=IVdata[IVdata[:,0].argsort()]   # Sort by voltages
    
    Isc = np.interp(0,IV_sorted[:,0],IV_sorted[:,1])
    
    return Isc

# Obtains Voc by linear interpolation around I=0
def get_Voc(IVdata):
    """Returns the Voc of the input raw I-V curve"""

   # Sort data
    IV_sorted = IVdata.copy()
    IV_sorted=IVdata[IVdata[:,1].argsort()]   # Sort by currents
    
    Voc = np.interp(0, IV_sorted[:,1],IV_sorted[:,0])
    
    return Voc

# Obtains the Pmax, and also the Vm and Im
def get_Pmax(IVdata):
  
   # Sort data and move to 1st quadrant
    IV_sorted = IVdata.copy()
 
    Isc = get_Isc(IV_sorted)
    if Isc<0:
        Isc*=-1
        IV_sorted[:,1]*=-1
    
    Voc = get_Voc(IV_sorted)
    if Voc<0:
        Voc*=-1
        IV_sorted[:,0]*=-1       
 
    IV_sorted=IV_sorted[IV_sorted[:,0].argsort()]   
   
 
    PV = IV_sorted.copy()
    PV[:,1] = IV_sorted[:,0]*IV_sorted[:,1]
    
    Pm = np.amax(PV[:,1])
    maxPosition = np.argmax(PV[:,1])
    Vm = PV[maxPosition,0]
    Im = IV_sorted[maxPosition,1]

    return Pm, Vm, Im

# Determines the slope of the I-V curve around Voc
# I-V curve is assumed to be in 1st quadrant
def getSlope_atVoc(data):
  
    # Find the first negative current point
    # Determine the range for the fit according to nPnts
    rI = np.where(data[:,1] <= 0)
    rangeI = np.asarray(rI)  
    rangeI = rangeI.flatten()   
    
    indexEnd = rangeI[0] + 2
    if indexEnd > len(data[:,0])-1:
        indexEnd = len(data[:,0])-1
    indexStart = rangeI[0] - 2
    if indexStart < 0:
        indexStart=0
    
    # Fit to a polynomial of degree 1
    polyCoeffs = np.polyfit(data[indexStart:indexEnd+1,0],data[indexStart:indexEnd+1,1],1)

    slope = polyCoeffs[0]
   
    # Create array with fit line
    slopeFitVoc = np.zeros((50,2))
    slopeFitVoc[:,0]=np.linspace(data[indexStart,0],data[indexEnd,0],50)
    slopeFitVoc[:,1] = np.polyval(polyCoeffs, slopeFitVoc[:,0])

    return slope, slopeFitVoc

# Returns the slope of I-V curve at Isc using
# a linear fit for a V range of +-Vrange     
def getSlope_atIsc(data):
    
    # Get data indexes in the Vrange
    rV = np.where(data[:,0] <= 0)
    rangeV = np.asarray(rV)  
    rangeV = rangeV.flatten()   

    indexStart = rangeV[-1] -5   
    if indexStart < 0:
        indexStart=0
    indexEnd = rangeV[-1] + 5
    if indexEnd > len(data[:,0])-1:
        indexEnd = len(data[:,0])-1
 
    # Fit to a polynomial of degree 1
    polyCoeffs = np.polyfit(data[indexStart:indexEnd+1,0],data[indexStart:indexEnd+1,1],1)
    #Take the slope from the linear fit
    slope = polyCoeffs[0]
 
    # Create array with fit line
    slopeFitIsc = np.zeros((50,2))
    slopeFitIsc[:,0]=np.linspace(data[indexStart,0],data[indexEnd,0],50)
    slopeFitIsc[:,1] = np.polyval(polyCoeffs, slopeFitIsc[:,0])


    return slope, slopeFitIsc

Now we can load the measured I-V curve and test the parameter extraction method

temperature = 25 #C
illum_IV = pd.read_csv('data/Illumination_I_V_curve.csv',  header=0) 
illum_IV.head()
V (V) I (A)
0 -0.3000 0.226923
1 -0.2555 0.226572
2 -0.2110 0.226254
3 -0.1665 0.225777
4 -0.1219 0.225872

We start by extracting the parameters used in the single-diode model of the solar cell.

IL_extr, I0_extr, n_extr, Rs_extr, Rp_extr, slopeFitIsc, slopeFitVoc = get_IV_params(illum_IV.values, temperature)

# Print the results 
print("Rp = " + f"{Rp_extr:.3f}")
print("Rs = " + f"{Rs_extr:.3e}")
print("I0 = " + f"{I0_extr:.3}") 
print("n = " + f"{n_extr:.3f}")
print("IL = " + f"{IL_extr:.3f}")
Rp = 322.470
Rs = -2.433e-02
I0 = 4.55e-09
n = 2.074
IL = 0.225

We can also extract short-circuit current \(I_{SC}\), open-circuit voltage \(V_{OC}\), voltage and current of the maximum power point \(V_{mp}\) and \(I_{mp}\), fill factor \(FF\), and efficiency \(\eta\).

Isc=get_Isc(illum_IV.values)
print("Isc = " + f"{Isc:.3f}" + " A")
Voc=get_Voc(illum_IV.values)
print("Voc = " + f"{Voc:.3f}"+ " V")
Pm,Vm,Im = get_Pmax(illum_IV.values)
print("Vmp = " + f"{Vm:.3f}"+ " V")
print("Imp = " + f"{Im:.3f}"+" A")
FF=(Vm*Im)/(Voc*Isc)
print("FF = " + f"{FF:.3f}")
Eff=Pm/(1000*10/10000)
print("Eff = " + f"{Eff:.3f}")
Isc = 0.225 A
Voc = 0.944 V
Vmp = 0.800 V
Imp = 0.209 A
FF = 0.787
Eff = 0.167

We can calculate the efficiency directly from the measured data and we check that we get the same value as previously calculated.

illum_IV['P (W)']=illum_IV['V (V)']*illum_IV['I (A)']
Eff_m=illum_IV['P (W)'].max()/(1000*10/10000)
print("Eff from direct measurements = " + f"{Eff_m:.3f}")
Eff from direct measurements = 0.167