import numpy as np
import os
# solcore imports
from solcore.structure import Layer
from solcore import material
from solcore import si
from rayflare.structure import Interface, BulkLayer, Structure
from rayflare.matrix_formalism import process_structure, calculate_RAT
from rayflare.utilities import get_savepath
from rayflare.transfer_matrix_method import tmm_structure
from rayflare.angles import theta_summary, make_angle_vector
from rayflare.textures import regular_pyramids
from rayflare.options import default_options
from solcore.material_system import create_new_material
import matplotlib.pyplot as plt
import matplotlib as mpl
import seaborn as sns
from sparse import load_npz
Section 8: Textured Si
This example is based on Figures 6, 7 and 8 from this paper. This compares three different structures, all based on a 200 micron thick slab of silicon with different surface textures:
- Planar front surface (TMM), crossed grating at rear (RCWA)
- Inverted pyramids on the front surface (RT_Fresnel), planar rear surface (TMM)
- Inverted pyramids on the front surface (RT_Fresnel), crossed grating at rear (RCWA)
The methods which will be used to calculate the redistribution matrices in each case are given in brackets. If case 1 and 2 are calculated first, then case 3 does not require the calculations of any additional matrices, since it will use the rear matrix from (1) and the front matrix from (2).
Setting up
First, importing relevant packages:
To make sure we are using the same optical constants for Si, load the same Si n/k data used in the paper linked above:
"Si_OPTOS", "data/Si_OPTOS_n.txt", "data/Si_OPTOS_k.txt",
create_new_material(=True) overwrite
Material created with optical constants n and k only.
You only need to do this one time, then the material will be stored in Solcore’s material database.
Setting options (taking the default options for everything not specified explicitly):
= 8 # same as in Fraunhofer paper
angle_degrees_in
= np.linspace(900, 1200, 20) * 1e-9
wavelengths
= material("Si_OPTOS")()
Si = material("Air")()
Air
= default_options()
options = wavelengths
options.wavelengths = angle_degrees_in * np.pi / 180 # incidence angle (polar angle)
options.theta_in = 50
options.n_theta_bins = 0.25
options.c_azimuth = 5e5 # number of rays per wavelength in ray-tracing
options.n_rays = "OPTOS_comparison"
options.project_name = 60 # number of RCWA orders to use (more = better convergence, but slower)
options.orders = "u" # unpolarized light
options.pol = False options.only_incidence_angle
Defining the structures
Now, set up the grating basis vectors for the RCWA calculations and define the grating structure. These are squares, rotated by 45 degrees. The halfwidth is calculated based on the area fill factor of the etched pillars given in the paper.
= 1000
x
= ((x, 0), (0, x))
d_vectors = 0.36
area_fill_factor = np.sqrt(area_fill_factor) * 500
hw
= [
back_materials =si("120nm"), material=Si,
Layer(width=[{"type": "rectangle", "mat": Air, "center": (x / 2, x / 2),
geometry"halfwidths": (hw, hw), "angle": 45}],
)]
Now we define the pyramid texture for the front surface in case (2) and (3) and make the four possible different surfaces: planar front and rear, front with pyramids, rear with grating. We specify the method to use to calculate the redistribution matrices in each case and create the bulk layer.
= regular_pyramids(elevation_angle=55, upright=False)
surf
= Interface(
front_surf_pyramids "RT_Fresnel",
=surf,
texture=[],
layers="inv_pyramids_front_" + str(options["n_rays"]),
name
)
= Interface("TMM", layers=[], name="planar_front")
front_surf_planar
= Interface(
back_surf_grating "RCWA",
=back_materials,
layers="crossed_grating_back",
name=d_vectors,
d_vectors=20,
rcwa_orders
)
= Interface("TMM", layers=[], name="planar_back")
back_surf_planar
= BulkLayer(200e-6, Si, name="Si_bulk") bulk_Si
fixed h 0.7140740033710572
Now we create the different structures and ‘process’ them (this will calculate the relevant matrices if necessary, or do nothing if it finds the matrices have previously been calculated and the files already exist). We don’t need to process the final structure because it will use matrices calculated for SC_fig6
and SC_fig7
.
= Structure(
SC_fig6 =Air, transmission=Air
[front_surf_planar, bulk_Si, back_surf_grating], incidence
)= Structure(
SC_fig7 =Air, transmission=Air
[front_surf_pyramids, bulk_Si, back_surf_planar], incidence
)= Structure(
SC_fig8 =Air, transmission=Air
[front_surf_pyramids, bulk_Si, back_surf_grating], incidence
)
='current')
process_structure(SC_fig6, options, save_location='current') process_structure(SC_fig7, options, save_location
Making matrix for planar surface using TMM for element 0 in structure
Existing angular redistribution matrices found
Existing angular redistribution matrices found
RCWA calculation for element 2 in structure
Existing angular redistribution matrices found
Ray tracing with Fresnel equations for element 0 in structure
Existing angular redistribution matrices found
Existing angular redistribution matrices found
Making matrix for planar surface using TMM for element 2 in structure
Existing angular redistribution matrices found
Calculating R/A/T
Then we ask RayFlare to calculate the reflection, transmission and absorption through matrix multiplication, and get the required result out (absorption in the bulk) for each cell. We also load the results from the reference paper to compare them to the ones calculated with RayFlare.
= calculate_RAT(SC_fig6, options, save_location='current')
results_fig6 = calculate_RAT(SC_fig7, options, save_location='current')
results_fig7 = calculate_RAT(SC_fig8, options, save_location='current')
results_fig8
= results_fig6[0]
RAT_fig6 = results_fig7[0]
RAT_fig7 = results_fig8[0]
RAT_fig8
= np.loadtxt("data/optos_fig6_sim.csv", delimiter=",")
sim_fig6 = np.loadtxt("data/optos_fig7_sim.csv", delimiter=",")
sim_fig7 = np.loadtxt("data/optos_fig8_sim.csv", delimiter=",") sim_fig8
Finally, we use TMM to calculate the absorption in a structure with a planar front and planar rear, as a reference.
= tmm_structure([Layer(si("200um"), Si)], incidence=Air, transmission=Air)
struc = False
options.coherent = ["i"]
options.coherency_list = tmm_structure.calculate(struc, options) RAT
Plotting
Plot everything together, including data from the reference paper for comparison:
= sns.color_palette("hls", 4)
palhf
= plt.figure()
fig 0], sim_fig6[:, 1],
plt.plot(sim_fig6[:, "--", color=palhf[0], label="OPTOS - rear grating (1)")
* 1e9, RAT_fig6["A_bulk"][0],
plt.plot(wavelengths "-o", color=palhf[0], label="RayFlare - rear grating (1)", fillstyle="none")
0], sim_fig7[:, 1],
plt.plot(sim_fig7[:, "--", color=palhf[1], label="OPTOS - front pyramids (2)",)
* 1e9, RAT_fig7["A_bulk"][0],
plt.plot(wavelengths "-o", color=palhf[1], label="RayFlare - front pyramids (2)", fillstyle="none")
0], sim_fig8[:, 1],
plt.plot(sim_fig8[:, "--", color=palhf[2], label="OPTOS - grating + pyramids (3)")
* 1e9, RAT_fig8["A_bulk"][0],
plt.plot(wavelengths "-o", color=palhf[2],label="RayFlare - grating + pyramids (3)", fillstyle="none",)
* 1e9, RAT["A_per_layer"][:, 0], "-k", label="Planar")
plt.plot(wavelengths ="lower left")
plt.legend(loc"Wavelength (nm)")
plt.xlabel("Absorption in Si")
plt.ylabel(900, 1200])
plt.xlim([0, 1])
plt.ylim([ plt.show()
We can see good agreement between the reference values and our calculated values. The structure with rear grating also behaves identically to the planar TMM reference case at the short wavelengths where front surface reflection dominates the result, as expected. Clearly, the pyramids perform much better overall, giving a large boost in the absorption at long wavelengths and also reducing the reflection significantly at shorter wavelengths. Plotting reflection and transmission emphasises this:
= plt.figure()
fig * 1e9,RAT_fig6["R"][0],
plt.plot(wavelengths "-o", color=palhf[0], label="RayFlare - rear grating (1)", fillstyle="none")
* 1e9, RAT_fig7["R"][0],
plt.plot(wavelengths "-o", color=palhf[1], label="RayFlare - front pyramids (2)", fillstyle="none")
* 1e9, RAT_fig8["R"][0],
plt.plot(wavelengths "-o", color=palhf[2], label="RayFlare - grating + pyramids (3)", fillstyle="none")
* 1e9, RAT_fig6["T"][0], "--o", color=palhf[0])
plt.plot(wavelengths * 1e9, RAT_fig7["T"][0], "--o", color=palhf[1])
plt.plot(wavelengths * 1e9, RAT_fig8["T"][0], "--o", color=palhf[2])
plt.plot(wavelengths
# these are just to create the legend:
-1, 0, "k-o", label="R", fillstyle="none")
plt.plot(-1, 0, "k--o", label="T")
plt.plot(
plt.legend()"Wavelength (nm)")
plt.xlabel("Reflected/transmitted fraction")
plt.ylabel(900, 1200])
plt.xlim([0, 0.6])
plt.ylim([ plt.show()
Redistribution matrices
Plot the redistribution matrix for the rear grating (summed over azimuthal angles) at 1100 nm:
= make_angle_vector(
theta_intv, phi_intv, angle_vector "n_theta_bins"], options["phi_symmetry"], options["c_azimuth"])
options[
= get_savepath(save_location='current', project_name=options.project_name)
path = load_npz(os.path.join(path, SC_fig6[2].name + "frontRT.npz"))
sprs
= 1100e-9
wl_to_plot = np.argmin(np.abs(wavelengths - wl_to_plot))
wl_index
= sprs[wl_index].todense()
full
= theta_summary(full, angle_vector, options["n_theta_bins"], "front")
summat = summat[: options["n_theta_bins"], :]
summat_r = summat_r.rename({ r"$\theta_{in}$": r"$\sin(\theta_{in})$",
summat_r r"$\theta_{out}$": r"$\sin(\theta_{out})$"})
= summat_r.assign_coords({r"$\sin(\theta_{in})$": np.sin(summat_r.coords[r"$\sin(\theta_{in})$"]).data,
summat_r r"$\sin(\theta_{out})$": np.sin(summat_r.coords[r"$\sin(\theta_{out})$"]).data})
= sns.cubehelix_palette(256, start=0.5, rot=-0.9)
palhf
palhf.reverse()= mpl.colors.ListedColormap(palhf)
seamap
= plt.figure()
fig = plt.subplot(111)
ax = summat_r.plot.imshow(ax=ax, cmap=seamap, vmax=0.3)
ax plt.show()
Questions
- If you can add only one of the textures (pyramids or a grating), which one is better? Why?
- Why do the structures with a front-surface texture have high reflection at long wavelengths? The anti-reflection properties of pyramids (treated with ray optics) are mostly independent of the wavelength, so why does apparent reflection increase’ near the bandgap of Si?
- Can you explain any of the features present in the angular redistribution matrix of the rear grating surface?