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:
Cotfas, D.T., Cotfas, P.A., Kaplanis, S., 2013. Methods to determine the dc parameters of solar cells: A critical review. Renew. Sustain. Energy Rev. 28, 588–596. https://doi.org/10.1016/j.rser.2013.08.017
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