def test_get_J_sc_SCR_vs_WL():
    from solcore.light_source import LightSource
    from solcore.interpolate import interp1d
    from solcore.analytic_solar_cells.depletion_approximation import get_J_sc_SCR_vs_WL

    light_source = LightSource(source_type="standard", version="AM1.5g")
    wl = np.linspace(300, 1800, 50) * 1e-9
    wl_ls, phg = light_source.spectrum(output_units='photon_flux_per_m', x=wl)

    xa_nm = np.random.uniform(1, 1000)
    xa = xa_nm * 1e-9
    xb = np.random.uniform(xa_nm + 1, 1100) * 1e-9

    ## make a simple Beer-Lambert profile
    dist = np.linspace(0, xb, 1000)
    alphas = np.linspace(1e5, 10, len(wl))

    expn = np.exp(-alphas[:, None] * dist[None, :])

    output = alphas[:, None] * expn
    output = output.T
    gen_prof = interp1d(dist, output, axis=0)

    zz = np.linspace(xa, xb, 1002)[:-1]
    gg = gen_prof(zz) * phg
    expected = np.trapz(gg, zz, axis=0)

    result = get_J_sc_SCR_vs_WL(xa, xb, gen_prof, wl, phg)

    assert expected == approx(result)
def test_get_J_sc_diffusion_vs_WL_bottom():
    from solcore.analytic_solar_cells.depletion_approximation import get_J_sc_diffusion_vs_WL
    from scipy.integrate import solve_bvp
    from solcore.interpolate import interp1d
    from solcore.light_source import LightSource

    D = np.power(10, np.random.uniform(-5, 0)) # Diffusion coefficient
    L = np.power(10, np.random.uniform(-9, -6)) # Diffusion length
    minority = np.power(10, np.random.uniform(-7, -4))# minority carrier density
    s = np.power(10, np.random.uniform(0, 3)) # surface recombination velocity

    light_source = LightSource(source_type="standard", version="AM1.5g")
    wl = np.linspace(300, 1800, 50) * 1e-9
    wl_ls, phg = light_source.spectrum(output_units='photon_flux_per_m', x=wl)

    xa_nm = np.random.uniform(1, 1000)
    xa = xa_nm * 1e-9
    xb = np.random.uniform(xa_nm + 1, 1100) * 1e-9

    ## make a simple Beer-Lambert profile
    dist = np.linspace(0, xb, 1000)
    alphas = np.linspace(1e8, 10, len(wl))

    expn = np.exp(- alphas[:, None] * dist[None, :])

    output = alphas[:, None] * expn
    output = output.T
    gen_prof = interp1d(dist, output, axis=0)

    zz = np.linspace(xa, xb, 1002)[:-1]
    gg = gen_prof(zz) * phg

    expected = np.zeros_like(wl)

    for i in range(len(wl)):
        if np.all(gg[:, i] == 0):  # no reason to solve anything if no generation at this wavelength
            expected[i] = 0

        A = lambda x: np.interp(x, zz, gg[:, i]) / D + minority / L ** 2

        def fun(x, y):
            out1 = y[1]
            out2 = y[0] / L ** 2 - A(x)
            return np.vstack((out1, out2))

        def bc(ya, yb):
            left = ya[0]
            right = yb[1] - s / D * (yb[0] - minority)
            return np.array([left, right])

        guess = minority * np.ones((2, zz.size))
        guess[1] = np.zeros_like(guess[0])
        solution = solve_bvp(fun, bc, zz, guess)

        expected[i] = solution.y[1][0]

    result = get_J_sc_diffusion_vs_WL(xa, xb, gen_prof, D, L, minority, s, wl, phg, side='bottom')

    assert result == approx(expected)
def test_wavelength_array_consistency():
    #GH 147
    from solcore.light_source import LightSource
    import numpy as np

    wl_arr = np.linspace(300, 1200, 10) * 1e-9
    wl_arr2 = np.linspace(300, 1200, 20) * 1e-9

    ls = LightSource(source_type='standard',
                     version='AM1.5g',
                     x=wl_arr,
                     output_units='photon_flux_per_m')
    assert ls.spectrum()[1].shape == wl_arr.shape
    assert ls.spectrum(x=wl_arr2)[1].shape == wl_arr2.shape
Example #4
0
    def evaluate(self, x):

        light_source = LightSource(source_type='standard', version='AM1.5g')

        wl = np.linspace(300, 1850, 500) * 1e-9

        solar_cell = self.make_cell(x)

        position = [1e-10] * 10 + [5e-8]

        V = np.linspace(0, 3.5, 300)
        solar_cell_solver(solar_cell,
                          'iv',
                          user_options={
                              'voltages': V,
                              'light_iv': True,
                              'wavelength': wl,
                              'mpp': True,
                              'light_source': light_source,
                              'optics_method': 'TMM',
                              'BL_correction': True,
                              'position': position
                          })

        efficiency = solar_cell.iv["Eta"]

        # print('Efficiency =', efficiency)

        return -efficiency
Example #5
0
    def __init__(self):

        # wavelength, materials.
        # make these attributes of 'self' so they can be accessed by the class object
        # here I am also creating lists of wavelengths and corresponding n and k data from
        # the Solcore materials - the reason for this is that there is currently an issue with using the Solcore
        # material class in parallel computations. Thus the information for the n and k data is saved here as a list
        # rather than a material object.
        self.wl = np.linspace(250, 900, 700)
        self.MgF2 = [
            self.wl,
            material('MgF2')().n(self.wl * 1e-9),
            material('MgF2')().k(self.wl * 1e-9)
        ]
        self.Ta2O5 = [
            self.wl,
            material('410', nk_db=True)().n(self.wl * 1e-9),
            material('410', nk_db=True)().k(self.wl * 1e-9)
        ]
        self.GaAs = [
            1000, self.wl,
            material('GaAs')().n(self.wl * 1e-9),
            material('GaAs')().k(self.wl * 1e-9)
        ]

        # assuming an AM0 spectrum
        spectr = LightSource(source_type='standard',
                             version='AM0',
                             x=self.wl,
                             output_units='photon_flux_per_m',
                             concentration=1).spectrum(self.wl * 1e-9)[1]

        self.spectrum = spectr / max(spectr)
Example #6
0
    def calculate_light_source(self) -> LightSource:
        """ Calculate the lightsource based on the chosen options

        :return: None
        """
        try:
            # First we create the x values
            x_min = self.x_min.get()
            x_max = self.x_max.get()
            x_points = self.x_points.get()
            concentration = self.concentration.get()

            # Now we retrieve the options
            options = self.spectrum.get_options()

        except Exception as err:
            # If any of the parameters causes problems, typically because there's text input where there should be a
            # number, we return the current light source
            print(err)
            return self.light_source

        # And now we are ready to create the spectrum
        try:
            light_source = LightSource(source_type=self.source_type.get(),
                                       x=np.linspace(x_min, x_max, x_points),
                                       output_units=self.units.get(),
                                       concentration=concentration,
                                       **options)
        except TypeError:
            return self.light_source

        return light_source
Example #7
0
    def plot_qe(self, data: pd.DataFrame, spectrum: LightSource) -> None:
        """ Plots the QE data and the different components, if any. If a spectrum is provided, it also calculates the Jsc of each junction

        :param data: Data to plot as a Pandas dataframe
        :param spectrum: A LightSource object with the spectrum, needed to calculate the Jsc of each junction
        :return: None
        """
        wl = data['WL'] * 1e9  # Wavelength in nanometers
        color = 'red'  # Default color; not really use
        i = 0  # Counter
        markerstyle = ('o', 'v', '^', '<', '>', '8', 's', 'p', '*', 'h', 'H', 'D', 'd', 'P', 'X')
        colors = ('r', 'g', 'b', 'm', 'c')
        losses = ('srh', 'rad', 'aug', 'surf', 'surb')
        losses_done = False

        # To accumulate the total light absorbed
        total_A = np.zeros_like(data['WL'])

        # And then the reflection
        y1 = 1 - data['R']
        y2 = np.ones_like(data['R'])
        self.axes.fill_between(wl, y1, y2, color='yellow', alpha=0.5, linewidth=3, label='R')

        # Now we plot what happens with light absorbed
        for name in data.columns.values:
            if 'A' in name:
                total_A += data[name]
                p = self.axes.plot(wl, data[name], '--', linewidth=3, label=name)
                color = p[-1].get_color()
                i = 0
                y1 = data[name]
            elif 'EQE' in name and len(name) <= 5:
                self.axes.plot(wl, data[name], linewidth=3, color=color, label=name)
            elif any(loss in name for loss in losses):
                # For the PDD model we have losses. We plot them as shaded areas
                label = losses[i] if (not losses_done and i < 5) else None
                y2 = y1
                y1 = y1 - data[name]
                self.axes.plot(wl, y1, ':', color=color)
                self.axes.fill_between(wl, y1, y2, facecolor=colors[i], alpha=0.5, label=label)
                i += 1
                losses_done = losses_done or i > 4
            elif 'EQE' in name:
                # And for the DA model we have contributions to the QE. We plot them with different markers.
                self.axes.plot(wl, data[name], color=color, marker=markerstyle[i], fillstyle='none', markevery=5,
                               label=name)
                i += 1

            # If a spectrum is given, we calculate the current related to each of the QE
            if spectrum is not None and name[:4] == 'EQE':
                current = data[name].values * spectrum.spectrum(x=wl, output_units='photon_flux_per_nm')[1]
                self.outputs[name] = q * np.trapz(current, wl)

        self.axes.plot(wl, total_A, 'k--', linewidth=2, label='Total A')

        self.axes.set_xlim(left=min(wl), right=max(wl))
        self.axes.set_ylim(bottom=0, top=1)
Example #8
0
def light_source(wavelength):
    from solcore.light_source import LightSource

    return LightSource(
        source_type="standard",
        version="AM1.5g",
        x=np.linspace(350, 1000, 301) * 1e-9,
        output_units="photon_flux_per_m",
        concentration=1,
    )
Example #9
0
    def __init__(self):
        # initialize an instance of the class; set some information which will be used in each iteration of the calculation:
        # materials, wavelengths, the light source

        T = 298
        wl = np.linspace(300, 1900, 800)

        # materials
        SiGeSn = material('SiGeSn')(T=T)

        GaAs = material('GaAs')(T=T)
        InGaP = material('GaInP')(In=0.5, T=T)
        Ge = material('Ge')(T=T)

        # make these attributes of 'self' so they can be accessed by the class object
        # here I am also creating lists of wavelengths and corresponding n and k data from
        # the Solcore materials - the reason for this is that there is currently an issue with using the Solcore
        # material class in parallel computations. Thus the information for the n and k data is saved here as a list
        # rather than a material object (see the documentation of OptiStack for the different acceptable formats
        # to pass optical constants for an OptiStack

        self.wl = wl
        self.SiGeSn = [
            self.wl,
            SiGeSn.n(self.wl * 1e-9),
            SiGeSn.k(self.wl * 1e-9)
        ]
        self.Ge = [self.wl, Ge.n(self.wl * 1e-9), Ge.k(self.wl * 1e-9)]

        self.InGaP = [
            self.wl, InGaP.n(self.wl * 1e-9),
            InGaP.k(self.wl * 1e-9)
        ]
        self.GaAs = [self.wl, GaAs.n(self.wl * 1e-9), GaAs.k(self.wl * 1e-9)]
        self.MgF2 = [
            self.wl,
            material('MgF2')().n(self.wl * 1e-9),
            material('MgF2')().k(self.wl * 1e-9)
        ]
        self.Ta2O5 = [
            self.wl,
            material('410', nk_db=True)().n(self.wl * 1e-9),
            material('410', nk_db=True)().k(self.wl * 1e-9)
        ]

        # assuming an AM1.5G spectrum
        self.spectr = LightSource(source_type='standard',
                                  version='AM1.5g',
                                  x=self.wl,
                                  output_units='photon_flux_per_nm',
                                  concentration=1).spectrum(self.wl)[1]
    def __init__(self):
        """ Make the wavelength and the materials n and k data object attributes.

        The n and k data are extracted from the Solcore materials rather than using
        the material directly because there is currently an issue with using the
        Solcore material class in parallel computations.
        """
        self.wl = np.linspace(250, 900, 700)
        self.MgF2 = [
            self.wl,
            material("MgF2")().n(self.wl * 1e-9),
            material("MgF2")().k(self.wl * 1e-9),
        ]
        # This is Ta2O5 from the SOPRA database included in Solcore
        self.Ta2O5 = [
            self.wl,
            material("TAOX1")().n(self.wl * 1e-9),
            material("TAOX1")().k(self.wl * 1e-9),
        ]
        self.GaAs = [
            1000,
            self.wl,
            material("GaAs")().n(self.wl * 1e-9),
            material("GaAs")().k(self.wl * 1e-9),
        ]

        # assuming an AM0 spectrum
        spectr = LightSource(
            source_type="standard",
            version="AM1.5g",
            x=self.wl,
            output_units="photon_flux_per_m",
            concentration=1,
        ).spectrum(self.wl * 1e-9)[1]

        # Only want to use spectrum to weight reflectivity result so don't care about
        # absolute values
        self.spectrum = spectr / max(spectr)
Example #11
0
import numpy as np
import matplotlib.pyplot as plt

from solcore.light_source import LightSource

# TODO If SMARTS is not installed, it keeps trying and producing errors.
#  LightSource is poorly designed...
# The wavelength range of the spectra
wl = np.linspace(300, 3000, 200)

# Now different types of light sources can be defined
gauss = LightSource(source_type='laser',
                    x=wl,
                    center=800,
                    linewidth=50,
                    power=200)
bb = LightSource(source_type='black body', x=wl, T=5800, entendue='Sun')
am15g = LightSource(source_type='standard', x=wl, version='AM1.5g')
spectral = LightSource(source_type='SPECTRAL2', x=wl)

# Plot comparing the different light sources
plt.figure(1)
plt.plot(*gauss.spectrum(), label='Gauss')
plt.plot(*bb.spectrum(), label='Black body')
plt.plot(*am15g.spectrum(), label='AM1.5G')
plt.plot(*spectral.spectrum(), label='SPECTRAL2')

try:
    smarts = LightSource(source_type='SMARTS', x=wl)
    plt.plot(*smarts.spectrum(), label='SMARTS')
except TypeError:
def test_get_J_sc_diffusion_green_bottom():
    from solcore.analytic_solar_cells.depletion_approximation import get_J_sc_diffusion_green
    from solcore.light_source import LightSource

    D = np.power(10, np.random.uniform(-5, 0))  # Diffusion coefficient
    L = np.power(10, np.random.uniform(-9, -6))  # Diffusion length
    minority = np.power(10, np.random.uniform(-7,
                                              -4))  # minority carrier density
    s = np.power(10, np.random.uniform(0,
                                       3))  # surface recombination velocityx

    light_source = LightSource(source_type="standard", version="AM1.5g")
    wl = np.linspace(300, 1800, 50) * 1e-9
    wl_ls, phg = light_source.spectrum(output_units='photon_flux_per_m', x=wl)

    xa_nm = np.random.uniform(1, 1000)
    xa = xa_nm * 1e-9
    xb = np.random.uniform(xa_nm + 1, 1100) * 1e-9
    beta1 = (xb - xa) / L
    if beta1 > 1.e2:
        xb = xa + 99. * L
        beta1 = (xb - xa) / L

    ## make a simple Beer-Lambert profile
    alphas = np.linspace(1e8, 10, len(wl))

    # theoreical value from Fonash, Solar cell device physics eq 4.15
    beta2 = (xb - xa) * alphas
    beta3 = -L * s / D
    c1 = beta2 * beta2 / (beta2 * beta2 - beta1 * beta1) * np.exp(-alphas * xa)
    c2 = (beta3 * beta1 / beta2 - 1.) / (beta3 * np.sinh(beta1) +
                                         np.cosh(beta1))
    expected = (beta3 * np.cosh(beta1) +
                np.sinh(beta1)) / (beta3 * np.sinh(beta1) + np.cosh(beta1))
    expected *= -beta1 / beta2
    expected += 1.
    expected = (expected + c2 * np.exp(-beta2)) * c1 * phg / D
    expected += minority / L * (beta3 * np.cosh(beta1) + np.sinh(beta1)) / (
        beta3 * np.sinh(beta1) + np.cosh(beta1))

    result = get_J_sc_diffusion_green(xa,
                                      xb,
                                      lambda x: alphas * np.exp(-alphas * x),
                                      D,
                                      L,
                                      minority,
                                      s,
                                      phg,
                                      side='bottom')

    assert beta1 < 1.e2
    assert result == approx(expected, rel=1.e-5)

    #  test version for extreme L values
    Lmax = np.log10((xb - xa) * 1.e-2)
    L = np.power(10, np.random.uniform(Lmax - 2., Lmax))

    # theoretical value for Beer-Lambert profile
    beta1 = (xb - xa) / L
    expected = np.exp(-beta1 - alphas * xb) - np.exp(-alphas * xa)
    expected *= -phg * alphas / D / (alphas + 1. / L)
    expected += minority / L

    result = get_J_sc_diffusion_green(xa,
                                      xb,
                                      lambda x: alphas * np.exp(-alphas * x),
                                      D,
                                      L,
                                      minority,
                                      s,
                                      phg,
                                      side='bottom')

    assert beta1 > 1.e2
    assert result == approx(expected, rel=1.e-5)
def test_get_J_sc_diffusion_green_top():
    from solcore.analytic_solar_cells.depletion_approximation import get_J_sc_diffusion_green
    from solcore.light_source import LightSource

    D = np.power(10, np.random.uniform(-5, 0))  # Diffusion coefficient
    L = np.power(10, np.random.uniform(-9, -6))  # Diffusion length
    minority = np.power(10, np.random.uniform(-7,
                                              -4))  # minority carrier density
    s = np.power(10, np.random.uniform(0, 3))  # surface recombination velocity

    light_source = LightSource(source_type="standard", version="AM1.5g")
    wl = np.linspace(300, 1800, 50) * 1e-9
    wl_ls, phg = light_source.spectrum(output_units='photon_flux_per_m', x=wl)

    xa_nm = np.random.uniform(1, 1000)
    xa = xa_nm * 1e-9
    xb = np.random.uniform(xa_nm + 1, 1100) * 1e-9
    beta1 = (xb - xa) / L
    if beta1 > 1.e2:
        xb = xa + 99. * L
        beta1 = (xb - xa) / L

    ## make a simple Beer-Lambert profile
    alphas = np.linspace(1e8, 10, len(wl))

    # theoreical value from Fonash, Solar cell device physics eq 4.10
    # (the absorption profile has to be offset for xa != 0.)
    beta2 = (xb - xa) * alphas
    beta3 = L * s / D
    c1 = beta2 * beta2 / (beta2 * beta2 - beta1 * beta1)
    c2 = (beta3 * beta1 / beta2 + 1.) / (beta3 * np.sinh(beta1) +
                                         np.cosh(beta1))
    expected = (beta3 * np.cosh(beta1) +
                np.sinh(beta1)) / (beta3 * np.sinh(beta1) + np.cosh(beta1))
    expected *= beta1 / beta2
    expected += 1.
    expected *= np.exp(-beta2)
    expected = (expected - c2) * c1 * phg / D * np.exp(-alphas * xa)
    expected -= minority / L * (beta3 * np.cosh(beta1) + np.sinh(beta1)) / (
        beta3 * np.sinh(beta1) + np.cosh(beta1))

    result = get_J_sc_diffusion_green(xa,
                                      xb,
                                      lambda x: alphas * np.exp(-alphas * x),
                                      D,
                                      L,
                                      minority,
                                      s,
                                      phg,
                                      side='top')

    assert beta1 < 1.e2
    assert result == approx(expected, rel=1.e-5)

    #  test version for extreme L values
    Lmax = np.log10((xb - xa) * 1.e-2)
    L = np.power(10, np.random.uniform(Lmax - 2., Lmax))

    # theoretical value for Beer-Lambert profile
    alphas_miL = alphas - 1. / L
    idx_c = np.abs(alphas_miL) < 1.e-3
    idx_i = np.logical_not(idx_c)
    expected = np.zeros_like(alphas)
    expected = np.exp(-alphas_miL * xa - xb / L) - np.exp(-alphas * xb)
    expected[idx_i] /= alphas_miL[idx_i]
    expected[idx_c] = np.exp(-alphas[idx_c] * xb)
    expected *= -phg * alphas / D
    expected -= minority / L

    result = get_J_sc_diffusion_green(xa,
                                      xb,
                                      lambda x: alphas * np.exp(-alphas * x),
                                      D,
                                      L,
                                      minority,
                                      s,
                                      phg,
                                      side='top')

    assert (xb - xa) / L > 1.e2
    assert result == approx(expected, rel=1.e-5)
Example #14
0
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm

from solcore.light_source import LightSource
from solcore.solar_cell import SolarCell
from solcore.solar_cell_solver import solar_cell_solver
from solcore.structure import Junction

# Illumination spectrum
wl = np.linspace(300, 4000, 4000) * 1e-9
light = LightSource(source_type="standard",
                    version="AM1.5g",
                    x=wl,
                    output_units="photon_flux_per_m")

T = 298
V = np.linspace(0, 5, 500)


# This function assembles the solar cell and calculates the IV cruve
def solve_MJ(EgBot, EgMid, EgTop):
    db_junction0 = Junction(kind="DB",
                            T=T,
                            Eg=EgBot,
                            A=1,
                            R_shunt=np.inf,
                            n=3.5)
    db_junction1 = Junction(kind="DB",
                            T=T,
                            Eg=EgMid,
Example #15
0
def da_light_source():
    from solcore.light_source import LightSource

    light_source = LightSource(source_type="standard", version="AM1.5g")
    return light_source
Example #16
0
    def plot(self, x):
        light_source = LightSource(source_type='standard', version='AM1.5g')

        wl = np.linspace(300, 1850, 500) * 1e-9

        solar_cell = self.make_cell(x)

        position = [1e-10] * 10 + [5e-8]

        V = np.linspace(0, 3.5, 300)
        solar_cell_solver(solar_cell,
                          'iv',
                          user_options={
                              'voltages': V,
                              'light_iv': True,
                              'wavelength': wl,
                              'mpp': True,
                              'light_source': light_source,
                              'optics_method': 'TMM',
                              'BL_correction': True,
                              'position': position
                          })

        efficiency = solar_cell.iv["Eta"]
        pmax = solar_cell.iv["Pmpp"]
        ff = solar_cell.iv["FF"]
        voc = solar_cell.iv["Voc"]
        isc = solar_cell.iv["Isc"]

        plt.figure()

        plt.plot(V,
                 solar_cell.iv['IV'][1] / 10,
                 'k',
                 linewidth=3,
                 label='Total')
        plt.plot(V, -solar_cell[2].iv(V) / 10, 'b', label='GaInP')
        plt.plot(V, -solar_cell[3].iv(V) / 10, 'g', label='GaAs')
        plt.plot(V, -solar_cell[4].iv(V) / 10, 'r', label='SiGeSn')
        plt.plot(V, -solar_cell[5].iv(V) / 10, 'y', label='Ge')
        plt.text(2, 10, '$\eta = $' + str(round(efficiency * 100, 1)) + '%')
        plt.text(2, 8, 'Pmax=' + str(round(pmax, 1)) + 'W/m$^2$')
        plt.text(2, 9, 'FF = ' + str(round(ff * 100, 1)) + '%')
        plt.text(2, 7, 'Voc=' + str(round(voc, 1)) + 'V')
        plt.text(2, 6, 'Jsc=' + str(round(0.1 * isc, 1)) + 'mA/cm$^2$')

        plt.legend()
        plt.ylim(0, 18)
        plt.xlim(0, 3.5)
        plt.ylabel('Current (mA/cm$^2$)')
        plt.xlabel('Voltage (V)')

        plt.show()

        # print('Efficiency =', efficiency)

        solar_cell_solver(solar_cell,
                          'qe',
                          user_options={
                              'wavelength': wl,
                              'optics_method': 'TMM',
                              'BL_correction': True,
                              'position': position
                          })

        plt.figure()
        plt.plot(wl * 1e9, solar_cell[2].eqe(wl) * 100, 'b', label='InGaP')
        plt.plot(wl * 1e9, solar_cell[3].eqe(wl) * 100, 'g', label='InGaAs')
        plt.plot(wl * 1e9, solar_cell[4].eqe(wl) * 100, 'r', label='SiGeSn')
        plt.plot(wl * 1e9, solar_cell[5].eqe(wl) * 100, 'y', label='Ge')
        plt.plot(wl * 1e9,
                 solar_cell.absorbed * 100,
                 'k--',
                 label='Absorption')
        # plt.plot(wl * 1e9, solar_cell[5].eqe(wl)*100, 'y', label='Ge')

        plt.legend(loc='upper right')
        plt.xlim(290, 1850)
        plt.ylim(0, 100)
        plt.ylabel('EQE (%)')
        plt.xlabel('Wavelength (nm)')
        plt.show()
Example #17
0
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm

from solcore.light_source import LightSource
from solcore.solar_cell import SolarCell
from solcore.solar_cell_solver import solar_cell_solver
from solcore.structure import Junction

# Illumination spectrum
wl = np.linspace(300, 4000, 4000) * 1e-9
light = LightSource(source_type='standard', version='AM1.5g', x=wl, output_units='photon_flux_per_m')

T = 298
V = np.linspace(0, 5, 500)


# This function assembles the solar cell and calculates the IV cruve
def solve_MJ(EgBot, EgMid, EgTop):
    db_junction0 = Junction(kind='DB', T=T, Eg=EgBot, A=1, R_shunt=np.inf, n=3.5)
    db_junction1 = Junction(kind='DB', T=T, Eg=EgMid, A=1, R_shunt=np.inf, n=3.5)
    db_junction2 = Junction(kind='DB', T=T, Eg=EgTop, A=1, R_shunt=np.inf, n=3.5)

    my_solar_cell = SolarCell([db_junction2, db_junction1, db_junction0], T=T, R_series=0)

    solar_cell_solver(my_solar_cell, 'iv',
                      user_options={'T_ambient': T, 'db_mode': 'top_hat', 'voltages': V, 'light_iv': True,
                                    'internal_voltages': np.linspace(-6, 5, 1100), 'wavelength': wl,
                                    'mpp': True, 'light_source': light})

    return my_solar_cell
def qe_depletion(junction, options):
    """ Calculates the QE curve of a junction object using the depletion approximation as described in J. Nelson, “The Physics of Solar Cells”, Imperial College Press (2003). The junction is then updated with an "iqe" and several "eqe" functions that calculates the QE curve at any wavelength.

    :param junction: A junction object.
    :param options: Solver options.
    :return: None.
    """

    science_reference(
        'Depletion approximation',
        'J. Nelson, “The Physics of Solar Cells”, Imperial College Press (2003).'
    )

    # First we have to figure out if we are talking about a PN, NP, PIN or NIP junction
    T = options.T
    kbT = kb * T

    id_top, id_bottom, pRegion, nRegion, iRegion, pn_or_np = identify_layers(
        junction)
    xn, xp, xi, sn, sp, ln, lp, dn, dp, Nd, Na, ni, es = identify_parameters(
        junction, T, pRegion, nRegion, iRegion)

    niSquared = ni**2

    Vbi = (kbT / q) * np.log(Nd * Na / niSquared) if not hasattr(
        junction, "Vbi") else junction.Vbi  # Jenny p146

    wn, wp = get_depletion_widths(junction, es, Vbi, 0, Na, Nd, xi)

    # Now it is time to calculate currents
    if pn_or_np == "pn":
        l_top, l_bottom = ln, lp
        w_top, w_bottom = wp, wn
        s_top, s_bottom = sp, sn
        d_top, d_bottom = dp, dn
        min_top, min_bot = niSquared / Na, niSquared / Nd
    else:
        l_bottom, l_top = ln, lp
        w_bottom, w_top = wp, wn
        s_bottom, s_top = sp, sn
        d_bottom, d_top = dp, dn
        min_bot, min_top = niSquared / Na, niSquared / Nd

    widths = []
    for layer in junction:
        widths.append(layer.width)

    cum_widths = np.cumsum([0] + widths)

    g = junction.absorbed
    wl = options.wavelength
    wl_sp, ph = LightSource(source_type='black body', x=wl,
                            T=6000).spectrum(output_units='photon_flux_per_m',
                                             x=wl)

    # The contribution from the Emitter (top side).
    xa = cum_widths[id_top]
    xb = cum_widths[id_top + 1] - w_top

    if options.da_mode == 'bvp':
        deriv = get_J_sc_diffusion_vs_WL(xa,
                                         xb,
                                         g,
                                         d_top,
                                         l_top,
                                         min_top,
                                         s_top,
                                         wl,
                                         ph,
                                         side='top')
    else:
        xbb = xb - (xb - xa) / 1001.
        deriv = get_J_sc_diffusion_green(xa,
                                         xbb,
                                         g,
                                         d_top,
                                         l_top,
                                         min_top,
                                         s_top,
                                         ph,
                                         side='top')
    j_sc_top = d_top * abs(deriv)

    # The contribution from the Base (bottom side).
    xa = cum_widths[id_bottom] + w_bottom
    xb = cum_widths[id_bottom + 1]

    if options.da_mode == 'bvp':
        deriv = get_J_sc_diffusion_vs_WL(xa,
                                         xb,
                                         g,
                                         d_bottom,
                                         l_bottom,
                                         min_bot,
                                         s_bottom,
                                         wl,
                                         ph,
                                         side='bottom')
    else:
        xbb = xb - (xb - xa) / 1001.
        deriv = get_J_sc_diffusion_green(xa,
                                         xbb,
                                         g,
                                         d_bottom,
                                         l_bottom,
                                         min_bot,
                                         s_bottom,
                                         ph,
                                         side='bottom')
    j_sc_bot = d_bottom * abs(deriv)

    # The contribution from the SCR (includes the intrinsic region, if present).
    xa = cum_widths[id_top + 1] - w_top
    xb = cum_widths[id_bottom] + w_bottom
    j_sc_scr = get_J_sc_SCR_vs_WL(xa, xb, g, wl, ph)

    # The total light absorbed, but not necessarily collected, is:
    xa = cum_widths[id_top]
    xb = cum_widths[id_bottom + 1]
    zz = np.linspace(xa, xb, 10001)
    gg = g(zz) * ph
    current_absorbed = np.trapz(gg, zz, axis=0)

    # Now, we put everything together
    j_sc = j_sc_top + j_sc_bot + j_sc_scr

    eqe = j_sc / ph
    eqe_emitter = j_sc_top / ph
    eqe_base = j_sc_bot / ph
    eqe_scr = j_sc_scr / ph

    iqe = j_sc / current_absorbed
    iqe[np.isnan(
        iqe
    )] = 0  # if zero current_absorbed, get NaN in previous line; want 0 IQE

    junction.iqe = interp1d(wl, iqe)

    junction.eqe = interp1d(wl,
                            eqe,
                            kind='linear',
                            bounds_error=False,
                            assume_sorted=True,
                            fill_value=(eqe[0], eqe[-1]))
    junction.eqe_emitter = interp1d(wl,
                                    eqe_emitter,
                                    kind='linear',
                                    bounds_error=False,
                                    assume_sorted=True,
                                    fill_value=(eqe_emitter[0],
                                                eqe_emitter[-1]))
    junction.eqe_base = interp1d(wl,
                                 eqe_base,
                                 kind='linear',
                                 bounds_error=False,
                                 assume_sorted=True,
                                 fill_value=(eqe_base[0], eqe_base[-1]))
    junction.eqe_scr = interp1d(wl,
                                eqe_scr,
                                kind='linear',
                                bounds_error=False,
                                assume_sorted=True,
                                fill_value=(eqe_scr[0], eqe_scr[-1]))

    junction.qe = State({
        'WL': wl,
        'IQE': junction.iqe(wl),
        'EQE': junction.eqe(wl),
        'EQE_emitter': junction.eqe_emitter(wl),
        'EQE_base': junction.eqe_base(wl),
        'EQE_scr': junction.eqe_scr(wl)
    })
Example #19
0
        Layer(width=si('200nm'), material=bsf, role="BSF")
    ],
                      sn=1e6,
                      sp=1e6,
                      T=T,
                      kind='PDD')

    return output


Vin = np.linspace(-2, 2.61, 201)
V = np.linspace(0, 2.6, 300)
wl = np.linspace(350, 1000, 301) * 1e-9
light_source = LightSource(source_type='standard',
                           version='AM1.5g',
                           x=wl,
                           output_units='photon_flux_per_m',
                           concentration=1)


class TestPDD(TestCase):
    def test_92_light_iv(self):
        answer = [
            141.434980729, 2.46952886616, 0.91434377329, 319.359951949,
            2.29565217391, 139.115130584, 0.319241623262
        ]
        with tempfile.TemporaryDirectory(
                prefix="tmp", suffix="_sc3TESTS") as working_directory:
            filename = os.path.join(working_directory, 'solcore_log.txt')
            PDD.log(filename)
Example #20
0
R_per_pass = np.sum(results_per_pass['r'][0], 2)
R_0 = R_per_pass[0]
R_escape = np.sum(R_per_pass[1:, :], 0)

# only select absorbing layers, sum over passes
results_per_layer_front = np.sum(results_per_pass['a'][0], 0)[:,[0,1,3,5,7,8]]

results_per_layer_back = np.sum(results_per_pass['a'][1], 0)


allres = np.flip(np.hstack((R_0[:,None], R_escape[:,None], results_per_layer_front, RAT['A_bulk'].T,
                    results_per_layer_back, RAT['T'].T)),1)

# calculated photogenerated current (Jsc with 100% EQE)

spectr_flux = LightSource(source_type='standard', version='AM1.5g', x=wavelengths,
                           output_units='photon_flux_per_m', concentration=1).spectrum(wavelengths)[1]

Jph_Si = q * np.trapz(RAT['A_bulk'][0] * spectr_flux, wavelengths)/10 # mA/cm2
Jph_Perovskite =  q * np.trapz(results_per_layer_front[:,3] * spectr_flux, wavelengths)/10 # mA/cm2

pal = sns.cubehelix_palette(13, start=.5, rot=-.9)
pal.reverse()

from scipy.ndimage.filters import gaussian_filter1d

ysmoothed = gaussian_filter1d(allres, sigma=2, axis=0)

bulk_A_text= ysmoothed[:,4]

# plot total R, A, T
fig = plt.figure(figsize=(5,4))
Example #21
0
    precedence goes to key value pairs in latter dicts.
    """
    result = State()
    for dictionary in dict_args:
        result.update(dictionary)
    return result


# General
default_options.T_ambient = 298
default_options.T = 298

# Illumination spectrum
default_options.wavelength = np.linspace(300, 1800, 251) * 1e-9
default_options.light_source = LightSource(source_type='standard',
                                           version='AM1.5g',
                                           x=default_options.wavelength,
                                           output_units='photon_flux_per_m')

# IV control
default_options.voltages = np.linspace(0, 1.2, 100)
default_options.mpp = False
default_options.light_iv = False
default_options.internal_voltages = np.linspace(-6, 4, 1000)
default_options.position = None
default_options.radiative_coupling = False

# Optics control
default_options.optics_method = 'BL'
default_options.recalculate_absorption = False

default_options = merge_dicts(default_options, ASC.db_options, PDD.pdd_options,
Example #22
0
        Layer(width=110e-9, material=MgF2, role="ARC1"),
        Layer(width=60e-9, material=ZnS, role="ARC2"),
        Layer(width=30e-9, material=window_top, role="window"),
        GaInP_junction,
        Layer(width=100e-9, material=bsf_top, role="BSF"),
        tunnel,
        GaAs_junction,
    ],
    T=T,
    substrate=n_GaAs,
)

light_source = LightSource(
    source_type="standard",
    version="AM1.5g",
    x=wl,
    output_units="photon_flux_per_m",
    concentration=1,
)

# The definitions are all done, so we just start solving the properties,
# starting with the QE. We calculate the QE curve under illumination
solar_cell_solver(
    my_solar_cell,
    "qe",
    user_options={
        "light_source": light_source,
        "wavelength": wl,
        "optics_method": "TMM",
    },
)
Example #23
0
import numpy as np
import matplotlib.pyplot as plt

from solcore import siUnits, material, si
from solcore.solar_cell import SolarCell
from solcore.structure import Junction, Layer
from solcore.solar_cell_solver import solar_cell_solver, default_options
from solcore.light_source import LightSource
from solcore.state import State

incidence_angle = 45  # should be in degrees
wl = np.linspace(290, 1900, 400) * 1e-9
concX = 566  # the light concentration
light_source = LightSource(
    source_type='standard',
    version='AM1.5d',
    x=default_options.wavelength,
    output_units='photon_flux_per_m',
    concentration=concX)  # define the input light source as AM1.5G

all_materials = []

Al2O3 = material('Al2O3')
TiO2 = material('TiO2')
AlInP = material("AlInP")
GaInP = material("GaInP")
GaAs = material('GaAs')
Ge = material("Ge")
Al02Ga08As = material('AlGaAs')
Al08Ga02As = material('AlGaAs')

# TOP CELL - GaInP
wavelengths = np.linspace(250, 1100, 80) * 1e-9

RCWA_wl = wavelengths

ropt = dict(LatticeTruncation='Circular',
            DiscretizedEpsilon=True,
            DiscretizationResolution=4,
            PolarizationDecomposition=False,
            PolarizationBasis='Default',
            LanczosSmoothing=True,
            SubpixelSmoothing=True,
            ConserveMemory=False,
            WeismannFormulation=True)

light_source = LightSource(source_type='standard', version='AM0')

options = State()
options['rcwa_options'] = ropt
options.optics_method = 'RCWA'
options.wavelength = wavelengths
options.light_source = light_source
options.pol = 's'
options.mpp = True
options.light_iv = True
options.position = 1e-10
options.voltages = np.linspace(-1.5, 1.5, 100)
options.size = size
options.orders = 20
options.parallel = True