import numpy as np
import matplotlib.pyplot as plt
from solcore import material, si
from solcore.solar_cell import Layer
from solcore.absorption_calculator import calculate_rat, OptiStack
import seaborn as sns
Example 1b: Basic cell optics
In this script, we will build on the TMM model from example 1(a) and look at the effects of interference.
Setting up
First, let’s define some materials:
= material("Si")
Si = material("Si3N4")()
SiN = material("Ag")() Ag
Note the second set of brackets (or lack thereof). The Solcore material system essentially operates in two stages; we first call the material
function with the name of the material we want to use, for example Si = material(“Si”), which creates a general Python class corresponding to that material. We then call this class to specify further details, such as the temperature, doping level, or alloy composition (where relavant). This happens below when defining Si_n and Si_p; both are use the Si class defined above, and adding further details to the material. For the definitions of SiN and Ag above, we do both steps in a single line, hence the two sets of brackets.
= Si(Nd=si("1e21cm-3"), hole_diffusion_length=si("10um"))
Si_n = Si(Na=si("1e16cm-3"), electron_diffusion_length=si("400um")) Si_p
To look at the effect of interference in the Si layer at different thicknesses, we make a list of thicknesses to test (evenly spaced on a log scale from 400 nm to 300 um):
= np.linspace(np.log(0.4e-6), np.log(300e-6), 8)
Si_thicknesses = np.exp(Si_thicknesses)
Si_thicknesses
= si(np.linspace(300, 1200, 400), "nm")
wavelengths
= {
options "recalculate_absorption": True,
"optics_method": "TMM",
"wavelength": wavelengths
}
Make a color palette using the seaborn package to make the plots look nicer
= sns.color_palette('rocket', n_colors=len(Si_thicknesses))
colors colors.reverse()
create an ARC layer:
= Layer(width=si('75nm'), material=SiN) ARC_layer
Effect of Si thickness
Now we are going to loop through the different Si thicknesses generated above, and create a simple solar cell-like structure. Because we will only do an optical calculation, we don’t need to define a junction and can just make a simple stack of layers.
We then calculate reflection, absorption and transmission (RAT) for two different situations: 1. a fully coherent stack 2. assuming the silicon layer is incoherent. This means that light which enters the Si layer cannot interfere with itself, but light in the ARC layer can still show interference. In very thick layers (much thicker than the wavelength of light being considered) this is likely to be more physically accurate because real light does not have infinite coherence length; i.e. if you measured wavelength-dependent transmission or reflection of a Si wafer hundreds of microns thick you would not expect to see interference fringes.
PLOT 1
plt.figure()
for i1, Si_t in enumerate(Si_thicknesses):
= Layer(width=Si_t, material=Si_p) # silicon layer
base_layer = OptiStack([ARC_layer, base_layer]) # OptiStack (optical stack) to feed into calculate_rat function
solar_cell
# Coherent calculation:
= calculate_rat(solar_cell, wavelengths*1e9, no_back_reflection=False) # coherent calculation
RAT_c # For historical reasons, Solcore's default setting is to ignore reflection at the back of the cell (i.e. at the
# interface between the final material in the stack and the substrate). Hence we need to tell the calculate_rat
# function NOT to ignore this reflection (no_back_reflection=False).
# Calculation assuming no interference in the silicon ("incoherent"):
= calculate_rat(solar_cell, wavelengths*1e9, no_back_reflection=False,
RAT_i =False, coherency_list=['c', 'i']) # partially coherent: ARC is coherent, Si is not
coherent
# Plot the results:
*1e9, RAT_c["A"], color=colors[i1], label=str(round(Si_t*1e6, 1)), alpha=0.7)
plt.plot(wavelengths*1e9, RAT_i["A"], '--', color=colors[i1])
plt.plot(wavelengths
=r"Thickness ($\mu$m)")
plt.legend(title300, 1300)
plt.xlim(0, 1.02)
plt.ylim("Absorption")
plt.ylabel("(1) Absorption in Si with varying thickness")
plt.title( plt.show()
We can see that the coherent calculations (solid lines) show clear interference fringes which depend on the Si thickness. The incoherent calculations do not have these fringes and seem to lie around the average of the interference fringes. For both sets of calculations, we see increasing absorption as the Si gets thicker, as expected.
Effect of reflective substrate
Now we repeat the calculation, but with an Ag substrate under the Si. Previously, we did not specify the substrate and so it was assumed by Solcore to be air (n=1, k=0).
PLOT 2
plt.figure()
for i1, Si_t in enumerate(Si_thicknesses):
= Layer(width=Si_t, material=Si_p)
base_layer
# As before, but now we specify the substrate to be silver:
= OptiStack([ARC_layer, base_layer], substrate=Ag)
solar_cell
= calculate_rat(solar_cell, wavelengths*1e9, no_back_reflection=False)
RAT_c = calculate_rat(solar_cell, wavelengths*1e9, no_back_reflection=False,
RAT_i =False, coherency_list=['c', 'i'])
coherent*1e9, RAT_c["A"], color=colors[i1],
plt.plot(wavelengths=str(round(Si_t*1e6, 1)), alpha=0.7)
label*1e9, RAT_i["A"], '--', color=colors[i1])
plt.plot(wavelengths
=r"Thickness ($\mu$m)")
plt.legend(title300, 1300)
plt.xlim(0, 1.02)
plt.ylim("Absorption")
plt.ylabel("(2) Absorption in Si with varying thickness (Ag substrate)")
plt.title( plt.show()
We see that the interference fringes get more prominent in the coherent calculation, due to higher reflection at the rear Si/Ag surface compared to Ag/Air. We also see a slightly boosted absorption at long wavelengths at all thicknesses, again due to improved reflection at the rear surface
Effect of polarization and angle of incidence
Finally, we look at the effect of incidence angle and polarization of the light hitting the cell.
PLOT 3
= [0, 30, 60, 70, 80, 89] # angles in degrees
angles
= Layer(width=si('75nm'), material=SiN)
ARC_layer = Layer(width=si("100um"), material=Si_p)
base_layer
= sns.cubehelix_palette(n_colors=len(angles))
colors
plt.figure()
for i1, theta in enumerate(angles):
= OptiStack([ARC_layer, base_layer])
solar_cell
= calculate_rat(solar_cell, wavelengths*1e9, angle=theta,
RAT_s ='s',
pol=False,
no_back_reflection=False, coherency_list=['c', 'i'])
coherent= calculate_rat(solar_cell, wavelengths*1e9, angle=theta,
RAT_p ='p',
pol=False,
no_back_reflection=False, coherency_list=['c', 'i'])
coherent
*1e9, RAT_s["A"], color=colors[i1], label=str(round(theta)))
plt.plot(wavelengths*1e9, RAT_p["A"], '--', color=colors[i1])
plt.plot(wavelengths
=r"$\theta (^\circ)$")
plt.legend(title300, 1300)
plt.xlim(0, 1.02)
plt.ylim("Absorption")
plt.ylabel("(3) Absorption in Si with varying thickness")
plt.title( plt.show()
For normal incidence (\(\theta = 0^\circ\)), s (solid lines) and p (dashed lines) polarization are equivalent. As the incidence angle increases, in general absorption is higher for p-polarized light (due to lower reflection). Usually, sunlight is modelled as unpolarized light, which computationally is usually done by averaging the results for s and p-polarized light.
Conclusions
We have now seen some effects of interference in layers of different thicknesses, and seen the effect of adding a highly reflective substrate. So we already have two strategies for light-trapping/improving the absorption in a solar cell: adding an anti-reflection coating (in example 1a), to reduce front-surface reflection and get more light into the cell, and adding a highly reflective layer at the back, to reduce loss through the back of the cell and keep light trapped in the cell.