import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import os
from solcore import si, material
from solcore.structure import Layer
from solcore.light_source import LightSource
from solcore.solar_cell import SolarCell
from solcore.constants import q
from solcore.absorption_calculator import search_db
from rayflare.rigorous_coupled_wave_analysis.rcwa import rcwa_structure
from rayflare.transfer_matrix_method.tmm import tmm_structure
from rayflare.options import default_options
Example 5a: Ultra-thin GaAs cell with diffraction grating
In previous examples, we have considered a few different methods used to improve absorption in solar cells: anti-reflection coatings, to decrease front-surface reflection, metallic rear mirrors to reduce transmission and increase the path length of light in the cell, and textured surfaces (with pyramids) which are used on Si cells to reduce reflection and increase the path length of light in the cell. Another method which can be used for light-trapping is the inclusion of periodic structures such as diffraction gratings or photonic crystals; here, we will consider an ultra-thin (80 nm) GaAs cell with a diffraction grating.
This example is based on the simulations done for this paper.
Note: This example requires that you have a working S4 installation.
Setting up
First, defining all the materials. We are just going to do an optical simulation, so don’t have to worry about doping levels and other parameters which would affect the electrical performance of the cell.
= material('AlInP')(Al=0.5) # In0.5Al0.5P
InAlP = material('GaAs')()
GaAs = material('GaInP')(In=0.5) # Ga0.5In0.5P
InGaP = material('Si3N4')() # for the ARC
SiN = material('Al2O3P')() # for the ARC
Al2O3
= material('Air')() Air
The optical constants used for the silver are very important, so we search for a known reliable dataset (from this paper).
= search_db(os.path.join("Ag", "Jiang"))[0][0]
Ag_pageid = material(str(Ag_pageid), nk_db=True)() Ag
Database file found at /Users/phoebe/.solcore/nk/nk.db
1 results found.
pageid shelf book page filepath hasrefractive hasextinction rangeMin rangeMax points
2 main Ag Jiang main/Ag/Jiang.yml 1 1 0.3 2.0 1701
AM0 spectrum (photon flux) for calculating currents. For space applications (i.e. above the atmosphere) we are often interested in AM0. We use Solcore’s LightSource class, then extract the AM0 photon flux at the wavelengths we are going to use in the simulations.
= np.linspace(303, 1000, 200) * 1e-9
wavelengths
= LightSource(source_type='standard', version='AM0', x=wavelengths, output_units="photon_flux_per_m")
AM0_ls = AM0_ls.spectrum(x=wavelengths)[1] # Photon flux; used to calculate photogenerated current later on AM0
Setting options. We choose s
polarization because, for normal incidence, there will not be a difference in the results for \(s\) and \(p\) polarization (and thus for unpolarized light, u
, which would be calculated as the average of the results for \(s\) and \(p\) polarization. We could set the polarization to u
for equivalent results (at normal incidence only), but this would take longer because then RayFlare has to run two calculations and then average them.
The other key option is the number of Fourier orders retained: rigorous coupled-wave analysis (RCWA) is a Fourier-space method, and we have to specify how many Fourier orders should be retained in the calculation. As we increase the number of orders, the calculation should become more accurate and eventually converge, but the computation time increases (it scales with the cube of the number of orders).
= default_options()
options = 's'
options.pol = wavelengths
options.wavelengths = 100 # Reduce the number of orders to speed up the calculation. options.orders
On-substrate device
This device is still on the GaAs substrate (it is also inverted compared to the other devices, since it represents the device before patterning and lift-off). We create the simulation object using tmm_structure
class, and then use the .calculate
function defined for the class to calculate the reflection, absorption per layer, and transmission.
print("Calculating on-substrate device...")
= SolarCell([Layer(si('20nm'), InAlP), Layer(si('85nm'), GaAs),
struct '20nm'), InGaP)])
Layer(si(
# make TMM structure for planar device
= tmm_structure(struct, incidence=Air, transmission=GaAs)
TMM_setup
# calculate
= TMM_setup.calculate(options)
RAT_TMM_onsubs
= RAT_TMM_onsubs['A_per_layer'][:,1] # absorption in GaAs
Abs_onsubs # indexing of A_per_layer is [wavelengths, layers]
= RAT_TMM_onsubs['R']
R_onsubs = RAT_TMM_onsubs['T'] T_onsubs
Calculating on-substrate device...
Planar silver mirror device
This device is now flipped (in fabrication terms, the back mirror was applied and then the device lifted off). It has a silver back mirror, which should increase reflection and the rear surface so that less light is lost to the substrate.
print("Calculating planar Ag mirror device...")
= SolarCell([Layer(material=InGaP, width=si('20nm')),
solar_cell_TMM =GaAs, width=si('85nm')),
Layer(material=InAlP, width=si('20nm'))],
Layer(material=Ag)
substrate
= tmm_structure(solar_cell_TMM, incidence=Air, transmission=Ag)
TMM_setup
= TMM_setup.calculate(options)
RAT_TMM
= RAT_TMM['A_per_layer'][:, 1]
Abs_TMM = RAT_TMM['A_per_layer'][:, 2]
Abs_TMM_InAlPonly = RAT_TMM['A_per_layer'][:, 0]
Abs_TMM_InGaPonly = RAT_TMM['R']
R_TMM = RAT_TMM['T'] T_TMM
Calculating planar Ag mirror device...
Database file found at /Users/phoebe/.solcore/nk/nk.db
Material main/Ag/Jiang.yml loaded.
Database file found at /Users/phoebe/.solcore/nk/nk.db
Material main/Ag/Jiang.yml loaded.
Nanophotonic grating device
Finally, this device has a grating made of silver and SiN, followed by a planar silver back mirror; this leads to diffraction, increasing the path length of light in the cell, while keeping reflection high. Here we use the rcwa_structure
class, which is used in the same way as tmm_structure
above and rt_structure
in Example 4a. Because we are now dealing with a structure which is periodic in the \(x\) and \(y\) directions (unlike the TMM structures, which are uniform in the plane), we have to specify the size of the unit cell (this does not have to be square; here we have a triangular unit cell to give a grating with a hexagonal layout of circles). Otherwise, the grating layer is specified as normal but with a geometry
argument which lists the shapes in the grating and the material they are made of.
There are many additional options which can be specified for S4 (which is used to actually run the RCWA calculations); more detail can be found in its documentation here.
print("Calculating nanophotonic grating device...")
= 600
x
# lattice vectors for the grating. Units are in nm!
= ((x, 0), (x / 2, np.sin(np.pi / 3) * x))
size
= dict(LatticeTruncation='Circular',
ropt =False,
DiscretizedEpsilon=8,
DiscretizationResolution=False,
PolarizationDecomposition='Default',
PolarizationBasis#LanczosSmoothing=dict(Power=2, Width=1),
=False,
LanczosSmoothing=False,
SubpixelSmoothing=False,
ConserveMemory=False,
WeismannFormulation=0)
Verbosity
= ropt
options.S4_options
# grating layers
= [Layer(width=si(100, 'nm'), material=SiN, geometry=[{'type': 'circle', 'mat': Ag, 'center': (0, 0),
grating 'radius': x/3, 'angle': 0}])] # actual grating part of grating
# DTL device without anti-reflection coating
= SolarCell([Layer(material=InGaP, width=si('20nm')),
solar_cell =GaAs, width=si('85nm')),
Layer(material=InAlP, width=si('20nm'))] + grating,
Layer(material=Ag)
substrate
# make RCWA structure
= rcwa_structure(solar_cell, size, options, Air, Ag)
S4_setup
# calculate
= S4_setup.calculate(options)
RAT
= RAT['A_per_layer'][:,1] # absorption in GaAs
Abs_DTL
= RAT['R']
R_DTL = RAT['T'] T_DTL
Calculating nanophotonic grating device...
Nanophotonic grating device with ARC
This device is exactly like the previous one, but with the additional of a simple single-layer anti-reflection coating.
print("Calculating nanophotonic grating device with ARC...")
# DTL device with anti-reflection coating
= SolarCell([Layer(material=Al2O3, width=si('70nm')),
solar_cell =InGaP, width=si('20nm')),
Layer(material=GaAs, width=si('85nm')),
Layer(material=InAlP, width=si('20nm'))] + grating,
Layer(material=Ag)
substrate
# make RCWA structure
= rcwa_structure(solar_cell, size, options, Air, Ag)
S4_setup
# calculate
= S4_setup.calculate(options)
RAT_ARC
= RAT_ARC['A_per_layer'][:,2] # absorption in GaAs + InGaP
Abs_DTL_ARC
= RAT_ARC['R']
R_DTL_ARC = RAT_ARC['T'] T_DTL_ARC
Calculating nanophotonic grating device with ARC...
Plotting results
PLOT 1: Comparing the absorption in GaAs for the four different device architectures
= sns.color_palette("husl", 4)
pal
= plt.figure(figsize=(6.4, 4.8))
fig
*1e9, 100*Abs_onsubs, color=pal[0], label="On substrate")
plt.plot(wavelengths*1e9, 100*Abs_TMM, color=pal[1], label="Planar mirror")
plt.plot(wavelengths*1e9, 100*Abs_DTL, color=pal[2], label="Nanophotonic grating (no ARC)")
plt.plot(wavelengths*1e9, 100*Abs_DTL_ARC, color=pal[3], label="Nanophotonic grating (with ARC)")
plt.plot(wavelengths
300, 950)
plt.xlim(0, 100)
plt.ylim('Wavelength (nm)')
plt.xlabel('EQE (%)')
plt.ylabel(='upper left')
plt.legend(loc plt.show()
We see that the addition of a planar silver mirror significantly boosts the absorption around 700 nm, essentially by creating a Fabry-Perot (thin film) cavity in the semiconductor layers through high reflection at the rear of the cell. The grating introduces multiple resonances relating to different diffraction orders and waveguide modes in the structure, which boosts the absorption especially close to the absorption edge in comparison to the planar devices.
PLOT 2: Absorption per layer in the planar Ag device.
= plt.figure(figsize=(6.4, 4.8))
fig *1e9,
plt.stackplot(wavelengths100*Abs_TMM, 100*Abs_TMM_InGaPonly, 100*Abs_TMM_InAlPonly],
[=pal,
colors=['Absorbed in GaAs', 'Absorbed in InGaP', 'Absorbed in InAlP'])
labels300, 950)
plt.xlim(0, 90)
plt.ylim('Wavelength (nm)')
plt.xlabel('EQE (%)')
plt.ylabel(='upper right')
plt.legend(loc plt.show()
The plot above shows that at short wavelengths, even very thin layers (in this case, 20 nm of InGaP) can absorb a very significant fraction of the incident radiation. Depending on the device, the carriers generated here may or may not be extracted as current.
Calculating photogenerated current
Finally, we use the photon flux in the AM0 spectrum to calculate the maximum possible achievable current for these devices.
= 0.1 * q * np.trapz(Abs_onsubs*AM0, wavelengths)
onsubs = 0.1 * q * np.trapz(Abs_TMM*AM0, wavelengths)
Ag = 0.1 * q * np.trapz(Abs_DTL*AM0, wavelengths)
DTL = 0.1 * q * np.trapz(Abs_DTL_ARC*AM0, wavelengths)
DTL_ARC
print('On substrate device current: %.1f mA/cm2 ' % onsubs)
print('Planar Ag mirror device current: %.1f mA/cm2 ' % Ag)
print('Nanophotonic grating (no ARC) device current: %.1f mA/cm2 ' % DTL)
print('Nanophotonic grating (with ARC) device current: %.1f mA/cm2 ' % DTL_ARC)
On substrate device current: 9.5 mA/cm2
Planar Ag mirror device current: 13.8 mA/cm2
Nanophotonic grating (no ARC) device current: 18.0 mA/cm2
Nanophotonic grating (with ARC) device current: 23.0 mA/cm2
As expected, simply adding a planar mirror boosts the current significantly. The addition of a nanophotonic grating gives a further boost (note that the grating we used here is optimized in terms of grating pitch (period) and dimension; not all gratings would give a boost in current, and some may even reduce performance due to e.g. parasitic absorption).