{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Single junction using the Poisson Drift Diffusion solver\n", "\n", "This example shows how to use the Poisson Drift Diffusion (PDD) solver to calculate the bandstructure, IV and QE curves of a single junction solar cell, a default GaAs solar cell built-in into Solcore. \n", "\n", "We start by importing all the required functions and modules:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "import numpy as np\n", "\n", "from solcore.solar_cell import SolarCell, default_GaAs\n", "from solcore.structure import Layer, Junction\n", "from solcore import si\n", "from solcore import material\n", "from solcore.light_source import LightSource\n", "from solcore.solar_cell_solver import solar_cell_solver" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, we define some variables we will need, including temperature, voltages and the illumination spectrum we will use in the calculation. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "T = 298\n", "substrate = material('GaAs')(T=T)\n", "V = np.linspace(0, 1.2, 60)\n", "wl = np.linspace(350, 1000, 301) * 1e-9\n", "light_source = LightSource(source_type='standard', version='AM1.5g', x=wl, output_units='photon_flux_per_m',\n", " concentration=1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, we create the solar cell itself. Since we only have one junction, we just put that one inside a list as the first input. To include more junctions (or extra layers) include them in the list. The order is important: it goes from the front surface of the cell to the substrate. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_solar_cell = SolarCell([default_GaAs(T)], T=T, R_series=0, substrate=substrate)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With the solar cell created, we can start to run different simulations on it starting, for example, by finding the bandstructure under equilibrium and plotting the doping and the electron and hole densities.\n", "\n", "We have use the index `0` to access the results of the only junction we have in the structure. If there are more, this will be accesible using index `1`, `2`, etc. In this case, the `offset` is not needed, because we only have one junction, but when there are more, it is necessary in order to shift the position to the correct location. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "solar_cell_solver(my_solar_cell, 'equilibrium')\n", "\n", "# We can plot the electron and hole densities in equilibrium and at short circuit, both calculated automatically\n", "# before calculating the IV curve\n", "plt.figure(1)\n", "zz = my_solar_cell[0].equilibrium_data.Properties['x'] + my_solar_cell[0].offset\n", "n = my_solar_cell[0].equilibrium_data.Properties['Nd']\n", "p = my_solar_cell[0].equilibrium_data.Properties['Na']\n", "plt.semilogy(zz, n, 'b')\n", "plt.semilogy(zz, p, 'r')\n", "\n", "zz = my_solar_cell[0].equilibrium_data.Bandstructure['x'] + my_solar_cell[0].offset\n", "n = my_solar_cell[0].equilibrium_data.Bandstructure['n']\n", "p = my_solar_cell[0].equilibrium_data.Bandstructure['p']\n", "plt.semilogy(zz, n, 'b--')\n", "plt.semilogy(zz, p, 'r--')\n", "\n", "plt.xlabel('Position (m)')\n", "plt.ylabel('Carrier density (m$^{-3}$)')\n", "\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next we calculate the IV curve of the same cell. We include several options here to configure the solver, such as the voltages and the spectrum we defined above, and also indicating that we want to solve the optics using a simple Beer-Lambert law (`BL`). We also ask that the maximum power point should be calculated (`'mpp': True`). " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "solar_cell_solver(\n", " my_solar_cell,\n", " \"iv\",\n", " user_options={\n", " \"voltages\": V,\n", " \"internal_voltages\": V,\n", " \"light_iv\": True,\n", " \"wavelength\": wl,\n", " \"optics_method\": \"BL\",\n", " \"mpp\": True,\n", " \"light_source\": light_source,\n", " },\n", ")\n", "\n", "plt.figure(2)\n", "plt.plot(V, -my_solar_cell[0].iv(V), \"r\", label=\"GaAs\")\n", "\n", "plt.legend()\n", "plt.xlim(0, 1.2)\n", "plt.ylim(0, 350)\n", "plt.xlabel(\"Bias (V)\")\n", "plt.ylabel(\"Current (A/m$^2}$)\")\n", "\n", "plt.text(\n", " 0.2,\n", " 100,\n", " \"Voc = {:4.2f} V\\n\"\n", " \"Isc = {:4.1f} A/m2\\n\"\n", " \"FF = {:4.1f} %\\n\"\n", " \"Pmpp = {:4.1f} W/m2\".format(\n", " my_solar_cell.iv[\"Voc\"],\n", " my_solar_cell.iv[\"Isc\"],\n", " my_solar_cell.iv[\"FF\"] * 100,\n", " my_solar_cell.iv[\"Pmpp\"],\n", " ),\n", ")\n", "\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, we calculate the QE curve. Again, we provide some inputs, like the solar spectrum to use. In this case, just for a change, we use the `TMM` optics solver, which gives a more realistic preformance by taking into account front surface recombination. Since we are changing the method use to calculate absorption, we need to force its recalculation.\n", "\n", "We do some fancy plotting to get a nicer representation of where the losses for my solar cell are coming from. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "solar_cell_solver(\n", " my_solar_cell,\n", " \"qe\",\n", " user_options={\n", " \"wavelength\": wl,\n", " \"optics_method\": \"TMM\",\n", " \"light_source\": light_source,\n", " \"recalculate_absorption\": True,\n", " \"position\": 1e-9,\n", " },\n", ")\n", "\n", "wl = wl * 1e9\n", "\n", "plt.figure(3)\n", "plt.plot(wl, 1 - my_solar_cell.reflected, \"b\")\n", "plt.fill_between(\n", " wl, 1 - my_solar_cell.reflected, 1, facecolor=\"blue\", alpha=0.6, label=\"Reflected\"\n", ")\n", "plt.fill_between(\n", " wl,\n", " 1 - my_solar_cell.reflected,\n", " my_solar_cell.absorbed,\n", " facecolor=\"yellow\",\n", " alpha=0.5,\n", " label=\"Transmitted\",\n", ")\n", "\n", "# EQE + fraction lost due to recombination in the front surface\n", "plt.plot(wl, my_solar_cell[0].qe.EQE + my_solar_cell[0].qe.EQEsurf, \"r\")\n", "plt.fill_between(\n", " wl,\n", " my_solar_cell[0].qe.EQE + my_solar_cell[0].qe.EQEsurf,\n", " my_solar_cell[0].qe.EQE,\n", " facecolor=\"red\",\n", " alpha=0.5,\n", " label=\"Front surface recombination\",\n", ")\n", "\n", "plt.plot(wl, my_solar_cell[0].qe.EQE + my_solar_cell[0].qe.EQEsurb, \"orange\")\n", "plt.fill_between(\n", " wl,\n", " my_solar_cell[0].qe.EQE + my_solar_cell[0].qe.EQEsurb,\n", " my_solar_cell[0].qe.EQE,\n", " facecolor=\"orange\",\n", " alpha=0.5,\n", " label=\"Back surface recombination\",\n", ")\n", "\n", "plt.plot(wl, my_solar_cell[0].qe.EQE, \"k\", linewidth=4)\n", "plt.fill_between(\n", " wl, my_solar_cell[0].qe.EQE, 0, facecolor=\"green\", alpha=0.5, label=\"EQE\"\n", ")\n", "\n", "plt.legend()\n", "plt.xlim(350, 950)\n", "plt.ylim(0, 1)\n", "plt.xlabel(\"Wavelength (nm)\")\n", "plt.ylabel(\"EQE (%/100)\")\n", "\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "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.7.4" } }, "nbformat": 4, "nbformat_minor": 4 }