{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from solcore import si\n", "from solcore import material\n", "from solcore.solar_cell import SolarCell, Layer, Junction\n", "from solcore.solar_cell_solver import solar_cell_solver, default_options\n", "import numpy as np\n", "from scipy.interpolate import interp1d\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "T = 300\n", "wavelengths_optics = np.linspace(300, 1200, 800)*1e-9" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Define some materials and layer widths:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Si = material(\"Si\")\n", "SiO2 = material(\"SiO2\")()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "n_material = Si(T=T, Nd=si(1e21, \"cm-3\"), hole_diffusion_length=si(\"50um\"),\n", " electron_mobility=50e-4, relative_permittivity = 11.68)\n", "p_material = Si(T=T, Na=si(1e16, \"cm-3\"), electron_diffusion_length=si(\"150um\"),\n", " hole_mobility=400e-4, relative_permittivity = 11.68)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ARC_width = si(\"100nm\")\n", "n_material_width = si(\"500nm\")\n", "p_material_width = si(\"50um\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We are first going to get some optics data which we can then use to illustrate how to provide\n", "the relevant quantities to the external optics solver. Obviously, this is a bit circular, since we\n", "are first going to use Solcore to calculate reflection and absorption and then feed that information\n", "back into solar_cell_solver in a different format, but this way we can show how the data should be\n", "provided when using external optics and that everything is working consistently." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "solar_cell = SolarCell(\n", " [\n", " Layer(width=ARC_width, material=SiO2),\n", " Junction([Layer(width=n_material_width, material=n_material, role='emitter'),\n", " Layer(width=p_material_width, material=p_material, role='base'),\n", "\t\t ], sn=1, sp=1, kind='DA'),\n", " ])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "total_width = ARC_width + n_material_width + p_material_width" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "options: we are going to use TMM to calculate the optics of a silicon cell with a 100 nm SiO2 anti-reflection coating
\n", "This will not be very realistic
\n", "compared to a standard Si cell, which will usually have some textured surface (usually pyramids) and not just a planar
\n", "surface with an anti-reflection coating, but it gives some physically reasonable data to use." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "options = default_options\n", "options.optics_method = \"TMM\"\n", "options.wavelength = wavelengths_optics\n", "# options.position = np.linspace(0, total_width, 100000)\n", "options.light_iv = True\n", "V = np.linspace(0, 1.2, 200)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "solar_cell_solver(solar_cell, 'iv', options)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we are going to get the relevant data to see how we might construct\n", "appropriate functions to provide external optics data to Solcore. If you are\n", "using experimental data, this could be loaded from a file, here we will take it\n", "from the TMM calculations done above. We get the fraction of light reflected,\n", "and the fraction of light absorbed in the Si layer." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "reflected = solar_cell.reflected\n", "absorbed_in_Si = solar_cell[1].layer_absorption" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We need to provide two attributes when defining the cell to give all the required\n", "information for external optics: external_reflected and external_absorbed. external_reflected<\n", "is a list of the fraction of incident light reflected at each wavelength that is specified in the user options.\n", "external_absorbed is a function which takes as input an array of positions (depths)\n", "in the cell in m and returns the differential absorption at each wavelength and depth; this is\n", "a 2D numpy array where the first dimension is over the wavelengths and the second dimension\n", "is over the positions in the cell." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We could take the diff_absorption method from solar_cell, which was constructed during the TMM\n", "calculation done above, but in order to show how the absorption profile can be calculated from\n", "total absorption in a layer (assuming Beer-Lambert law absorption, i.e. no interference in the layer)\n", "we will define a function which calculates the differential absorption from just the total absorption\n", "and the absorption coefficient of the junction material." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To illustrate how you can use data from e.g. an experiment to perform calculations at different wavelength,\n", "we're going to take the data we calculated above but use slightly different wavelength points." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Interpolate reflection and total absorption in Si to the new wavelengths:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "interp_ref = interp1d(options.wavelength, reflected)\n", "interp_totalA = interp1d(options.wavelength, solar_cell[1].layer_absorption)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "wavelengths_external = np.linspace(301, 1199, 800)*1e-9" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "alpha = n_material.alpha(wavelengths_external)\n", "A_layer = interp_totalA(wavelengths_external)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Make function which returns the absorption profile. The functional form can be found by differentiating the
\n", "Beer-Lambert law, and making sure it is normalized correctly to give the expected total absorption" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "junction_width = n_material_width + p_material_width" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def make_absorb_fn(alpha, A_layer, junction_width):\n", " norm = A_layer * alpha / (1 - np.exp(-alpha * junction_width))\n", " def profile(z):\n", " xy = norm[None, :] * np.exp(-alpha * z[:, None])\n", " return xy.T\n", " return profile" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "diff_absorb_fn = make_absorb_fn(alpha, A_layer, junction_width)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We now define a solar cell for the external optics calculation. It's the same as solar_cell,\n", "but without the ARC layer; this does not do anything in the electrical calculation, and we must\n", "omit it so that the diff_absorb_fn defined above works correctly. This function should describe the\n", "differential absorption profile in the WHOLE cell, including any surface layers,\n", "but to avoid complexity in make_absorb_fn it just calculates\n", "Beer-Lambert absorption in the Si and ignores the ARC. So for the absorption profile to match the actual\n", "cell, we need the front surface to be the Si emitter." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "solar_cell_external = SolarCell(\n", " [\n", " Junction([Layer(width=n_material_width, material=n_material, role='emitter'),\n", " Layer(width=p_material_width, material=p_material, role='base'),\n", "\t\t ], sn=1, sp=1, kind='DA'),\n", " ], external_reflected=interp_ref(wavelengths_external), external_absorbed=diff_absorb_fn)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "options.optics_method = \"external\"\n", "options.wavelength = wavelengths_external" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "solar_cell_solver(solar_cell_external, 'iv', options)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Check that the total absorption and reflection are the same. The total reflection will be the same
\n", "as it is directly supplied by the user, but the total reflection is calculated by integrating the
\n", "differential absorption, so this is a good check:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.figure()\n", "plt.plot(wavelengths_external*1e9, solar_cell_external.reflected, label='Reflected - external')\n", "plt.plot(wavelengths_external*1e9, solar_cell_external.absorbed, label='Absorbed - external')\n", "plt.plot(wavelengths_optics*1e9, reflected, 'k--', label='Reflected - TMM')\n", "plt.plot(wavelengths_optics*1e9, absorbed_in_Si, '--', label='Absorbed - TMM')\n", "plt.legend()\n", "plt.xlabel('Wavelength (nm)')\n", "plt.ylabel('R/A')\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Compare the light-IV curves:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.figure(1)\n", "plt.plot(V, -solar_cell[1].iv(V), 'b', label='TMM calculation')\n", "plt.plot(V, -solar_cell_external[0].iv(V), 'k--', label='External optics')\n", "plt.legend()\n", "plt.ylim(-20, 350)\n", "plt.xlim(0, 1)\n", "plt.ylabel('Current (A/m$^2$)')\n", "plt.xlabel('Voltage (V)') #The expected values of Isc and Voc are 372 A/m^2 and 0.63 V respectively" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, compare the absorption profiles (diff_absorption). These will not be exactly identical,\n", "because the profile for the TMM cell also contains the absorption profile in the ARC (= 0 everywhere),\n", "so is shifted over by 100 nm." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "position_plot = np.linspace(0, 200, 100)*1e-9" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "absorption_profile_TMM = solar_cell[0].diff_absorption(position_plot)\n", "absorption_profile_constructed = solar_cell_external[0].diff_absorption(position_plot)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.figure(figsize=(10, 4))\n", "plt.subplot(121)\n", "plt.imshow(absorption_profile_TMM, aspect='auto', extent=[0, 400, 1200, 300])\n", "plt.colorbar()\n", "plt.xlabel('Depth (nm)')\n", "plt.ylabel('Wavelength (nm)')\n", "plt.title(r'Differential absorption (m$^{-1}$) in front surface of cell')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.subplot(122)\n", "plt.imshow(absorption_profile_constructed, aspect='auto', extent=[0, 400, 1200, 300])\n", "plt.xlabel('Depth (nm)')\n", "plt.ylabel('Wavelength (nm)')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.show()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.4" } }, "nbformat": 4, "nbformat_minor": 2 }