from solcore import material, si
from solcore.absorption_calculator import search_db, download_db
import os
from solcore.structure import Layer, Junction
from solcore.solar_cell_solver import solar_cell_solver, default_options
from solcore.solar_cell import SolarCell
from solcore.light_source import LightSource
from solcore.constants import q
import numpy as np
import matplotlib.pyplot as plt
Section 7: Planar GaInP//Si tandem cell
This example is partly based on the structure presented in this paper, but with planar interfaces instead of a textured Si surface. This is a four-terminal GaInP/Si device which uses an epoxy and glass to bond the two cells together mechanically. First, we will do optical-only calculations to look at the effect of an intermediate anti-reflection coating (on top of the epoxy/glass) on the absorption in the bottom Si cell, and then we will use the results of the optical calculation to do a device simulation and calculate external quantum efficiency and current-voltage under AM1.5G.
Note: the paper linked above has a GaInP/AlGaInP heterojunction as the top junction. Because we do not have AlGaInP built in to Solcore’s database, this is replaced by a GaInP homojunction in this example.
Defining materials, layers and junctions
The paper referenced above uses a double-layer anti-reflection coating (ARC) made of MgF\(_2\) and ZnS. As in the previous example, we use the interface to the refractiveindex.info database to select optical constant data from specific sources, and define Solcore materials using this data. The III-V materials are taken from Solcore’s own material database.
Note that for the epoxy/glass layer, we use only a single material (BK7 glass). The epoxy and glass used in the paper have the same refractive index (n = 1.56), so we can use a single material with an appropriate refractive index to represent them.
# uncomment to download database download_db()
= search_db(os.path.join("MgF2", "Rodriguez-de Marcos"))[0][0];
MgF2_pageid = search_db(os.path.join("ZnS", "Querry"))[0][0];
ZnS_pageid = material(str(MgF2_pageid), nk_db=True)();
MgF2 = material(str(ZnS_pageid), nk_db=True)();
ZnS
= material("AlInP")(Al=0.52)
window = material("GaInP")
GaInP = material("AlGaAs")(Al=0.5)
BSF
= material("BK7")() epoxy
For the Si cell, the front surface has both a low-index and high-index SiN\(x\) layer. The rear surface uses Al\(2\)O\(3\), and the cell has Al at the rear surface.
= material("SiO")()
SiOx = search_db("Vogt-1.91")[0][0];
SiN_191_pageid = search_db("Vogt-2.13")[0][0];
SiN_213_pageid = material(str(SiN_191_pageid), nk_db=True)();
SiN_191 = material(str(SiN_213_pageid), nk_db=True)();
SiN_213
= material("Si")
Si
= material("Al2O3P")()
Al2O3 = material("Al")() Al
Database file found at /Users/phoebe/.solcore/nk/nk.db
1 results found.
pageid shelf book page filepath hasrefractive hasextinction rangeMin rangeMax points
2816 other SiN Vogt-1.91 anti-reflective coatings/SiN/Vogt-1.91.yml 1 1 0.25 1.7 146
Database file found at /Users/phoebe/.solcore/nk/nk.db
1 results found.
pageid shelf book page filepath hasrefractive hasextinction rangeMin rangeMax points
2818 other SiN Vogt-2.13 anti-reflective coatings/SiN/Vogt-2.13.yml 1 1 0.25 1.7 146
We now define the layers used in the top cell stack: the ARC and window layer for the top cell, and the GaInP junction itself. The ARC and window layers are not involved in the electrical calculation using the depletion approximation, so they are defined as simple layers, while the GaInP emitter and base are defined as part of a Junction
object.
= [
ARC_window 97e-9, MgF2),
Layer(41e-9, ZnS),
Layer(17e-9, window, role="window"),
Layer(
]
= Junction([
GaInP_junction 200e-9, GaInP(In=0.50, Nd=si("2e18cm-3"), hole_diffusion_length=si("300nm")),
Layer(="emitter"),
role750e-9, GaInP(In=0.50, Na=si("1e17cm-3"), electron_diffusion_length=si(
Layer("800nm")),
="base"),
role500e-9, BSF, role="bsf")], kind="DA", sn=1, sp=1
Layer( )
We now define the spacer layer, with and without a ZnS anti-reflection coating, so we can compare their performance in the cell stack. Note that we set the epoxy thickness here to be 10 microns, although the real thickness is much higher - this is because the epoxy/glass is not absorbing at the wavelengths which are able to reach it (which are not absorbed in the GaInP top cell), and we will treat it incoherently (no thin-film interference), so the exact thickness does not matter.
= [
spacer 82e-9, ZnS),
Layer(10e-6, epoxy), # real thickness is much higher, but since this layer is
Layer(# non-absorbing at the relevant wavelength (> 650 nm) and treated incoherently,
# this does not matter
]
= [
spacer_noARC 10e-6, epoxy),
Layer( ]
Now we define the layer stacks for the Si cell, including the front SiO\(_x\)/SiN\(_x\) stack, the junction itself, and the back dielectric layers.
= [
Si_front_surf 100e-9, SiOx),
Layer(70e-9, SiN_191),
Layer(15e-9, SiN_213),
Layer(
]
= Junction([
Si_junction 1e-6, Si(Nd=si("2e18cm-3"), hole_diffusion_length=2e-6), role="emitter"),
Layer(150e-6, Si(Na=si("2e15cm-3"), electron_diffusion_length=150e-6), role="base"),
Layer(="DA", sn=0.1, sp=0.1)
], kind
= [
Si_back_surf 15e-9, Al2O3),
Layer(120e-9, SiN_191)
Layer( ]
Comparing the optical performance with and without intermediate ARC
Now we will run the calculation. We will treat some of the layers (those above the epoxy) with a coherent TMM calculation, and the epoxy and the layers below it using incoherent TMM. We will discuss the difference this makes, why this is important, and when to use coherent and incoherent layers.
= len(ARC_window + GaInP_junction)
n_coh_layers = 1 + len(Si_front_surf + Si_junction + Si_back_surf)
n_inc_layers
= np.linspace(300, 1200, 600) * 1e-9
wl
= default_options
options = True
options.recalculate_absorption = wl
options.wavelength = 'TMM'
options.optics_method
= LightSource(source_type="standard", version="AM1.5g", x=wl*1e9,
AM15G ="photon_flux_per_nm") output_units
Now we define two versions of the cell for optical calculations, without and with the ZnS anti-reflection coating on the epoxy. Note that we also set the substrate for the calculation (aluminium) here.
= SolarCell(
cell_no_ARC + GaInP_junction + spacer_noARC + Si_front_surf + Si_junction +
ARC_window
Si_back_surf,=Al,
substrate
)
= SolarCell(
cell_with_ARC + GaInP_junction + spacer + Si_front_surf + Si_junction + Si_back_surf,
ARC_window =Al,
substrate )
We set the appropriate coherency list for the structure (a list with entry ‘c’ for a coherent layer or ‘i’ for an incoherent layer), and solve for the cell optics of the cell without the intermediate ARC. We get the total absorption in the GaInP and Si junctions.
= ['c']*(n_coh_layers) + ['i']*n_inc_layers
options.coherency_list "optics", options)
solar_cell_solver(cell_no_ARC,
= cell_no_ARC[3].layer_absorption + cell_no_ARC[4].layer_absorption
GaInP_A = cell_no_ARC[10].layer_absorption + cell_no_ARC[11].layer_absorption Si_A
As above, but for the cell with an intermediate ARC:
= ["c"]*(n_coh_layers + 1) + ['i']*n_inc_layers
options.coherency_list "optics", options)
solar_cell_solver(cell_with_ARC,
= cell_with_ARC[3].layer_absorption + cell_with_ARC[4].layer_absorption
GaInP_A_ARC = cell_with_ARC[11].layer_absorption + cell_with_ARC[12].layer_absorption Si_A_ARC
Treating layer(s) 12 incoherently
Calculating RAT...
Calculating absorption profile...
Now we plot the GaInP and Si absorption, and the reflectance of the whole structure, for both cells:
plt.figure()* 1e9, GaInP_A, "--k")
plt.plot(wl * 1e9, Si_A, "--r")
plt.plot(wl * 1e9, cell_no_ARC.reflected, '--b')
plt.plot(wl
* 1e9, GaInP_A_ARC, "-k", label="GaInP")
plt.plot(wl * 1e9, Si_A_ARC, "-r", label="Si")
plt.plot(wl * 1e9, cell_with_ARC.reflected, '-b', label="R")
plt.plot(wl ='upper right')
plt.legend(loc"Wavelength (nm)")
plt.xlabel("Absorptance/Reflectance")
plt.ylabel(
plt.tight_layout() plt.show()
We see that the cell without an ARC on the epoxy shows much stronger interference fringes (due to the thickness of the top stack), and higher reflectance overall in the long-wavelength region (at short wavelengths, light is absorbed before it is able to reach the epoxy at all). Before doing an actual electrical calculation, we will calculate the limiting current in both of the sub-cells (assuming all the generated charge carriers can be collected):
= q*np.trapz(GaInP_A * AM15G.spectrum()[1], wl*1e9)
J_GaInP = q*np.trapz(Si_A * AM15G.spectrum()[1], wl*1e9)
J_Si
print("Limiting short-circuit currents without ARC (mA/cm2): {:.1f} / {:.1f}".format(
/10, J_Si/10))
J_GaInP
= q*np.trapz(GaInP_A_ARC * AM15G.spectrum()[1], wl*1e9)
J_GaInP_ARC = q*np.trapz(Si_A_ARC * AM15G.spectrum()[1], wl*1e9)
J_Si_ARC
print("Limiting short-circuit currents with ARC (mA/cm2): {:.1f} / {:.1f}".format(
/10, J_Si_ARC/10)) J_GaInP_ARC
Limiting short-circuit currents without ARC (mA/cm2): 16.4 / 15.5
Limiting short-circuit currents with ARC (mA/cm2): 16.4 / 17.4
As expected from the reduced reflection and increased absorption in the Si, the cell with an intermediate ARC has significantly higher maximum current in the bottom Si cell.
EQE and IV calculation
Now, just taking the structure with an intermediate ARC, we do a cell calculation using the depletion approximation.
= True
options.mpp = True
options.light_iv = np.linspace(0, 1.9, 100)
options.voltages = AM15G
options.light_source
= SolarCell(
solar_cell + [GaInP_junction] + spacer + Si_front_surf + [Si_junction] +
ARC_window
Si_back_surf,=Al,
substrate )
First, we calculate and plot the external quantum efficiency (EQE):
'qe', options)
solar_cell_solver(solar_cell,
plt.figure()* 1e9, GaInP_A_ARC, "--k", label="GaInP only absorption")
plt.plot(wl * 1e9, Si_A_ARC, "--r", label="Si only absorption")
plt.plot(wl *1e9, solar_cell[3].eqe(wl), '-k')
plt.plot(wl*1e9, solar_cell[9].eqe(wl), '-r')
plt.plot(wl='upper right')
plt.legend(loc"Wavelength (nm)")
plt.xlabel("Absorptance/EQE")
plt.ylabel(0,1)
plt.ylim(
plt.tight_layout() plt.show()
Treating layer(s) 12 incoherently
Calculating RAT...
Calculating absorption profile...
Solving QE of the solar cell...
/Users/phoebe/Documents/develop/solcore-education/venv/lib/python3.11/site-packages/solcore/analytic_solar_cells/depletion_approximation.py:617: RuntimeWarning: invalid value encountered in divide
iqe = j_sc / current_absorbed
And the current-voltage under AM1.5G:
'iv', options)
solar_cell_solver(solar_cell,
plt.figure()'IV'][1]/10, 'k', linewidth=3,
plt.plot(options.voltages, solar_cell.iv[='Total (2-terminal)')
label-solar_cell[3].iv(options.voltages)/10, 'b',
plt.plot(options.voltages, ='GaInP')
label-solar_cell[9].iv(options.voltages)/10, 'g',
plt.plot(options.voltages, ='Si')
label0.1, 18, r"2-terminal $\eta$ = {:.2f}%".format(solar_cell.iv["Eta"]*100))
plt.text(
plt.legend()0, 20)
plt.ylim(0, 1.9)
plt.xlim('Current (mA/cm$^2$)')
plt.ylabel('Voltage (V)')
plt.xlabel(
plt.tight_layout() plt.show()
Treating layer(s) 12 incoherently
Calculating RAT...
Calculating absorption profile...
Solving IV of the junctions...
Solving IV of the tunnel junctions...
Solving IV of the total solar cell...
Two vs. four-terminal efficiency
By default, Solcore assumes any SolarCell object is a two-terminal device, and will thus calculate the total I-V curve assuming the cells are connected in series and that the current is limited by the lowest-current sub-cell. However, it will also calculate the I-V curves of the individual cells, so we can use this information to calculate the possible power output in a 4-terminal configuration where the cells operate independently from an electrical point of view:
= np.linspace(0, 1.3, 100)
V = V*solar_cell[3].iv(V)
P_GaInP = V*solar_cell[9].iv(V)
P_Si
= max(-P_GaInP)
P_MPP_GaInP
= max(-P_Si)
P_MPP_Si
= (P_MPP_GaInP + P_MPP_Si)/AM15G.power_density
eta_4T
print('4-terminal efficiency: {:.1f} %'.format(eta_4T*100))
4-terminal efficiency: 23.6 %
Questions/challenges
- What causes the strange, sharp fringes in the simulation data of Fig. 1 in the reference paper? Can you reproduce them by modifying this code? Which version of the simulation do you think is more correct, and why?
- How could you increase the current in one or both of the sub-cells (remember, unlike the paper, we assumed all the layers in the cell are planar!).
- Once the light encounters an ‘incoherent’ (thick) layer, does it make sense to treat any layers below that as coherent?