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
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)
예제 #4
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)
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)
예제 #7
0
# 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:
    pass

plt.xlim(300, 3000)
plt.xlabel('Wavelength (nm)')
plt.ylabel('Power density (Wm$^{-2}$nm$^{-1}$)')
plt.tight_layout()
plt.legend()