Ejemplo n.º 1
0
def solve_tmm(solar_cell, options):
    """ Calculates the reflection, transmission and absorption of a solar cell object using the transfer matrix method. Internally, it creates an OptiStack and then it calculates the optical properties of the whole structure.

    :param solar_cell: A solar_cell object
    :param options: Options for the solver
    :return: None
    """
    wl = options.wavelength

    # We include the shadowing losses
    initial = (1 - solar_cell.shading) if hasattr(solar_cell, 'shading') else 1

    # Now we calculate the absorbed and transmitted light. We first get all the relevant parameters from the objects
    all_layers = []
    for j, layer_object in enumerate(solar_cell):

        # Attenuation due to absorption in the AR coatings or any layer in the front that is not part of the junction
        if type(layer_object) is Layer:
            all_layers.append(layer_object)

        # For each junction, and layer within the junction, we get the absorption coefficient and the layer width.
        elif type(layer_object) in [TunnelJunction, Junction]:
            for i, layer in enumerate(layer_object):
                all_layers.append(layer)

    # With all the information, we create the optical stack
    no_back_reflexion = options.no_back_reflexion if 'no_back_reflexion' in options.keys() else True
    stack = OptiStack(all_layers, no_back_reflexion=no_back_reflexion)

    #dist = np.logspace(-1, np.log10(solar_cell.width * 1e9), int(1000 * np.log10(solar_cell.width * 1e9)))
    dist = np.arange(0, solar_cell.width * 1e9, 1)
    # print(len(dist))
    position = options.position if 'position' in options.keys() else dist

    print('Calculating RAT...')
    RAT = calculate_rat(stack, wl * 1e9, coherent=True)

    print('Calculating absorption profile...')
    out = calculate_absorption_profile(stack, wl * 1e9, dist=position)

    # With all this information, we are ready to calculate the differential absorption function
    diff_absorption, all_absorbed = calculate_absorption_tmm(out)

    # Each building block (layer or junction) needs to have access to the absorbed light in its region.
    # We update each object with that information.
    for j in range(len(solar_cell)):
        solar_cell[j].diff_absorption = diff_absorption
        solar_cell[j].absorbed = types.MethodType(absorbed, solar_cell[j])

    solar_cell.reflected = RAT['R'] * initial
    solar_cell.transmitted = (1 - RAT['R'] - all_absorbed) * initial
    solar_cell.absorbed = all_absorbed * initial
Ejemplo n.º 2
0
def test_TMM_rat():
    GaAs = material("GaAs")(T=300)

    my_structure = Structure([Layer(si(3000, "nm"), material=GaAs)])

    wavelength = np.linspace(450, 1100, 300)
    idx = np.argmin(abs(wavelength - 800))

    out = calculate_rat(
        my_structure, wavelength, coherent=True, no_back_reflexion=False
    )
    out = (out["R"][idx], out["A"][idx], out["T"][idx])

    data = (0.33328918841743332, 0.65996607786373396, 0.0067447337188326914)

    assert all([d == approx(o) for d, o in zip(data, out)])
Ejemplo n.º 3
0
    def test_42_TMM_rat(self):
        GaAs = material('GaAs')(T=300)

        my_structure = Structure([
            Layer(si(3000, 'nm'), material=GaAs),
        ])

        wavelength = np.linspace(450, 1100, 300)
        idx = np.argmin(abs(wavelength - 800))

        out = calculate_rat(my_structure,
                            wavelength,
                            coherent=True,
                            no_back_reflexion=False)
        out = (out['R'][idx], out['A'][idx], out['T'][idx])

        data = (0.33328918841743332, 0.65996607786373396,
                0.0067447337188326914)

        for i in range(3):
            self.assertAlmostEqual(data[i], out[i])
Ejemplo n.º 4
0
def calculate_optics(device, wavelengths, dist=None):
    """ Uses the transfer matrix solver to calculate the optical properties of the structure: that is, the reflection
    and the absorption as a function of the position.

    :param device: A device structure
    :param wavelengths: The wavelengths at which to calculate the optical information (in m)
    :param dist: The positions at which to calculate the absorption (in m). If None, it is calculated internally.
    :return: A dictionary with the reflection, the position, the wavelengths and the absorption as a function of
    the wavelength and position.
    """

    output = {}
    output['wavelengths'] = wavelengths
    wl = wavelengths * 1e9  # Input is in meters but the calculators use nm
    if dist is None:
        d = dist
    else:
        d = dist * 1e9  # Input is in meters but the calculators use nm

    rat = calculate_rat(device, wl)
    output['R'] = rat['R']

    absorption = calculate_absorption_profile(device, wl, dist=d)

    output['absorption'] = absorption['absorption'] * 1e9
    output['position'] = absorption['position'] * 1e-9

    optics_thickness = 0
    for layer in device['layers']:
        if layer['label'] in ['optics', 'Optics']:
            optics_thickness += layer['properties']['width']
        else:
            break

    output['position'] -= optics_thickness

    return output
Ejemplo n.º 5
0
# Build the optical stack...
stack = Structure([[117, 1240 / E_eV, mgf_nk[1], mgf_nk[2]],
                   [80, 1240 / E_eV, sic_nk[1], sic_nk[2]],
                   [61, 1240 / E_eV, zns_nk[1], zns_nk[2]],
                   [25, 1240 / E_eV, alinp_nk[1], alinp_nk[2]],
                   [350000, 1240 / E_eV, gainp_nk[1], gainp_nk[2]]])

angles = np.linspace(0, 80, 10)
RAT_angles = []

print("Calculate RAT ::")
for theta in angles:
    print("Calculating at angle :: %4.1f deg" % theta)
    # Calculate RAT data...
    rat_data = calculate_rat(stack, angle=theta, wavelength=1240 / E_eV)

    RAT_angles.append((theta, rat_data["R"], rat_data["A"]))

colors = plt.cm.jet(np.linspace(1, 0, len(RAT_angles)))

fig, ax2 = plt.subplots(1, 1)

for i, RAT in enumerate(RAT_angles):
    ax2.plot(1240 / E_eV,
             RAT[1] * 100,
             ls="-",
             color=colors[i],
             label="%4.1f$^\circ$" % RAT[0])
    ax2.plot(1240 / E_eV, RAT[2] * 100, ls="--", color=colors[i])
plt.xlabel('Wavelength (nm)')
plt.ylabel('Reflection / Absorption')
#plt.legend()
plt.show()

from solcore.absorption_calculator import calculate_rat

## pure TMM
all_layers = front_materials + [Layer(bulkthick, Ge)] + back_materials

coh_list = len(front_materials) * ['c'] + ['i'] + ['c']

TMM_res = calculate_rat(all_layers,
                        wavelength=wavelengths * 1e9,
                        substrate=Air,
                        no_back_reflection=False,
                        angle=options['theta_in'] * 180 / np.pi,
                        coherent=False,
                        coherency_list=coh_list,
                        pol=options['pol'])

plt.figure()
plt.plot(options['wavelengths'] * 1e9, TMM_res['R'], label='R')
plt.plot(options['wavelengths'] * 1e9, TMM_res['T'], label='T')
plt.plot(options['wavelengths'] * 1e9,
         TMM_res['A_per_layer'][len(front_materials) + 1],
         label='Ge')
plt.plot(options['wavelengths'] * 1e9,
         TMM_res['A_per_layer'][1] + TMM_res['A_per_layer'][2],
         label='ARC')
plt.plot(options['wavelengths'] * 1e9,
         TMM_res['A_per_layer'][3],
Ejemplo n.º 7
0
def solve_tmm(solar_cell, options):
    """ Calculates the reflection, transmission and absorption of a solar cell object using the transfer matrix method.
    Internally, it creates an OptiStack and then it calculates the optical properties of the whole structure.
    A substrate can be specified in the SolarCell object, which is treated as a semi-infinite transmission medium.
    Shading can also be specified (as a fraction).

    Relevant options are 'wl' (the wavelengths, in m), the incidence angle 'theta' (in degrees), the polarization 'pol' ('s',
    'p' or 'u'), 'position' (locations in m at which depth-dependent absorption is calculated), 'no_back_reflexion' and 'BL_correction'.
    'no_back_reflexion' sets whether reflections from the back surface are suppressed (if set to True, the default),
    or taken into account (if set to False).

    If 'BL_correction' is set to True, thick layers (thickness > 10*maximum wavelength) are treated incoherently using
    the Beer-Lambert law, to avoid the calculation of unphysical interference oscillations in the R/A/T spectra.

    A coherency_list option can be provided: this should have elements equal to the total number of layers (if a Junction
    contains multiple Layers, each should have its own entry in the coherency list). Each element is either 'c' for coherent
    treatment of that layer or 'i' for incoherent treatment.

    :param solar_cell: A SolarCell object
    :param options: Options for the solver
    :return: None
    """
    wl = options.wavelength
    BL_correction = options.BL_correction if 'BL_correction' in options.keys(
    ) else True
    theta = options.theta if 'theta' in options.keys(
    ) else 0  # angle IN DEGREES
    pol = options.pol if 'pol' in options.keys() else 'u'

    # We include the shadowing losses
    initial = (1 - solar_cell.shading) if hasattr(solar_cell, 'shading') else 1

    # Now we calculate the absorbed and transmitted light. We first get all the relevant parameters from the objects
    all_layers = []
    widths = []
    n_layers_junction = []

    for j, layer_object in enumerate(solar_cell):

        # Attenuation due to absorption in the AR coatings or any layer in the front that is not part of the junction
        if type(layer_object) is Layer:
            all_layers.append(layer_object)
            widths.append(layer_object.width)
            n_layers_junction.append(1)

        # For each junction, and layer within the junction, we get the absorption coefficient and the layer width.
        elif type(layer_object) in [TunnelJunction, Junction]:
            n_layers_junction.append(len(layer_object))
            for i, layer in enumerate(layer_object):
                all_layers.append(layer)
                widths.append(layer.width)

    # With all the information, we create the optical stack
    no_back_reflexion = options.no_back_reflexion if 'no_back_reflexion' in options.keys(
    ) else True

    full_stack = OptiStack(all_layers,
                           no_back_reflexion=no_back_reflexion,
                           substrate=solar_cell.substrate)

    if 'coherency_list' in options.keys():
        coherency_list = options.coherency_list
        coherent = False
        assert len(coherency_list) == full_stack.num_layers, \
            'Error: The coherency list must have as many elements (now {}) as the ' \
            'number of layers (now {}).'.format(len(coherency_list), full_stack.num_layers)
    else:
        coherency_list = None
        coherent = True

    if BL_correction and any(
            widths > 10 *
            np.max(wl)):  # assume it's safe to ignore interference effects
        make_incoherent = np.where(np.array(widths) > 10 * np.max(wl))[0]
        print('Treating layer(s) ' + str(make_incoherent).strip('[]') +
              ' incoherently')
        if not 'coherency_list' in options.keys():
            coherency_list = np.array(len(all_layers) * ['c'])
            coherent = False
        else:
            coherency_list = np.array(coherency_list)
        coherency_list[make_incoherent] = 'i'
        coherency_list = coherency_list.tolist()

    position = options.position * 1e9
    profile_position = position[position < sum(full_stack.widths)]

    print('Calculating RAT...')
    RAT = calculate_rat(full_stack,
                        wl * 1e9,
                        angle=theta,
                        coherent=coherent,
                        coherency_list=coherency_list,
                        no_back_reflexion=no_back_reflexion,
                        pol=pol)

    print('Calculating absorption profile...')
    out = calculate_absorption_profile(full_stack,
                                       wl * 1e9,
                                       dist=profile_position,
                                       angle=theta,
                                       no_back_reflexion=no_back_reflexion,
                                       pol=pol,
                                       coherent=coherent,
                                       coherency_list=coherency_list)

    # With all this information, we are ready to calculate the differential absorption function
    diff_absorption, all_absorbed = calculate_absorption_tmm(out, initial)

    # Each building block (layer or junction) needs to have access to the absorbed light in its region.
    # We update each object with that information.
    layer = 0
    A_per_layer = np.array(
        RAT['A_per_layer'][1:-1])  # first entry is R, last entry is T
    for j in range(len(solar_cell)):

        solar_cell[j].diff_absorption = diff_absorption
        solar_cell[j].absorbed = types.MethodType(absorbed, solar_cell[j])
        solar_cell[j].layer_absorption = initial * np.sum(
            A_per_layer[layer:(layer + n_layers_junction[j])], axis=0)
        layer = layer + n_layers_junction[j]

    solar_cell.reflected = RAT['R'] * initial
    solar_cell.absorbed = sum(
        [solar_cell[x].layer_absorption for x in np.arange(len(solar_cell))])
    solar_cell.transmitted = initial - solar_cell.reflected - solar_cell.absorbed
Ejemplo n.º 8
0
plt.show()

inc_angle = angle_vector[angle_index, 1] * 180 / np.pi

from solcore.absorption_calculator import OptiStack, calculate_rat

layers = [Layer(500e-9, GaAs), Layer(200e-9, Ge)]

OptSt = OptiStack(layers,
                  no_back_reflection=False,
                  substrate=Si,
                  incidence=Air)

RAT = calculate_rat(OptSt,
                    options['wavelengths'] * 1e9,
                    angle=inc_angle,
                    no_back_reflection=False,
                    pol=options['pol'])

Ge_e = np.real((Ge.n(wavelengths) + 1j * Ge.k(wavelengths))**2)
GaAs_e = np.real((GaAs.n(wavelengths) + 1j * GaAs.k(wavelengths))**2)

plt.figure()
plt.plot(wavelengths * 1e9, RAT['A_per_layer'][1] / Af_1, label='A1 TMM/RCWA')
plt.plot(wavelengths * 1e9, RAT['A_per_layer'][2] / Af_2, label='A2 TMM/RCWA')
plt.plot(wavelengths * 1e9, RAT['R'] / Rf, label='R TMM/RCWA')
plt.plot(wavelengths * 1e9, RAT['T'] / Tf, label='T TMM/RCWA')
#plt.plot(wavelengths*1e9, 1/GaAs.n(wavelengths))
#plt.plot(wavelengths*1e9, 1/Ge.n(wavelengths))
#plt.plot(wavelengths*1e9, Af_1, '--', label='Af_1')
#plt.plot(wavelengths*1e9, Af_2, ':', label='Af_2')
Ejemplo n.º 9
0
def solve_tmm(solar_cell, options):
    """ Calculates the reflection, transmission and absorption of a solar cell object using the transfer matrix method

    :param solar_cell:
    :param options:
    :return:
    """
    wl = options.wavelength

    # We include the shadowing losses
    initial = (1 - solar_cell.shading) if hasattr(solar_cell, 'shading') else 1

    # Now we calculate the absorbed and transmitted light. We first get all the relevant parameters from the objects
    widths = []
    offset = 0
    all_layers = []
    for j, layer_object in enumerate(solar_cell):

        # Attenuation due to absorption in the AR coatings or any layer in the front that is not part of the junction
        if type(layer_object) is Layer:
            all_layers.append(layer_object)
            widths.append(layer_object.width)

        # For each junction, and layer within the junction, we get the absorption coefficient and the layer width.
        elif type(layer_object) in [TunnelJunction, Junction]:
            junction_width = 0
            try:
                for i, layer in enumerate(layer_object):
                    all_layers.append(layer)
                    junction_width += layer.width
                    widths.append(layer.width)

                solar_cell[j].width = junction_width

            except TypeError as err:
                print(
                    'ERROR in "solar_cell_solver: TMM solver":\n'
                    '\tNo layers found in Junction or TunnelJunction objects.')
                raise err

        solar_cell[j].offset = offset
        offset += layer_object.width

    # With all the information, we create the optical stack
    no_back_reflexion = options.no_back_reflexion if 'no_back_reflexion' in options.keys(
    ) else True
    stack = OptiStack(all_layers, no_back_reflexion=no_back_reflexion)

    dist = np.logspace(0, np.log10(offset * 1e9),
                       int(300 * np.log10(offset * 1e9)))
    position = options.position if 'position' in options.keys() else dist

    print('Calculating RAT...')
    RAT = calculate_rat(stack, wl * 1e9, coherent=True)

    print('Calculating absorption profile...')
    out = calculate_absorption_profile(stack, wl * 1e9, dist=position)

    # With all this information, we are ready to calculate the differential absorption function
    diff_absorption, all_absorbed = calculate_absorption_tmm(out)

    # Each building block (layer or junction) needs to have access to the absorbed light in its region.
    # We update each object with that information.
    for j in range(len(solar_cell)):
        solar_cell[j].diff_absorption = diff_absorption
        solar_cell[j].absorbed = types.MethodType(absorbed, solar_cell[j])

    solar_cell.reflected = RAT['R'] * initial
    solar_cell.transmitted = (1 - RAT['R'] - all_absorbed) * initial
    solar_cell.absorbed = all_absorbed * initial
Ejemplo n.º 10
0
    Layer(50e-9, GaAs),
    Layer(20e-6, Si),
    Layer(100E-9, GaInP),
    Layer(70e-9, GaAs)
]

from solcore.absorption_calculator import OptiStack, calculate_rat

optist = OptiStack(layers,
                   incidence=Air,
                   substrate=SiN,
                   no_back_reflection=False)
TMM_res = calculate_rat(optist,
                        options['wavelengths'] * 1e9,
                        angle=options['theta_in'] * 180 / np.pi,
                        pol=options['pol'],
                        coherent=False,
                        coherency_list=['c', 'c', 'c', 'i', 'c', 'c'],
                        no_back_reflection=False)

plt.figure()
plt.plot(options['wavelengths'] * 1e9, TMM_res['R'], label='R')
plt.plot(options['wavelengths'] * 1e9, TMM_res['T'], label='T')
plt.plot(options['wavelengths'] * 1e9,
         TMM_res['A_per_layer'][4],
         label='A_bulk (Si)')
plt.plot(options['wavelengths'] * 1e9, TMM_res['A_per_layer'][1], label='SiN')
plt.plot(options['wavelengths'] * 1e9,
         TMM_res['A_per_layer'][2],
         label='InGaP')
plt.plot(options['wavelengths'] * 1e9, TMM_res['A_per_layer'][3], label='GaAs')
Ejemplo n.º 11
0
e_inf = [3.7883, 3.8915, 3.8982]
A = [16.038, 36.556, 36.806]
Br = [0.11112, 0.10413, 0.088618]
label = ['150', '250', '350']
ls = ['solid', 'dashed', 'dashdot']

for i in range(len(e_inf)):

    # We create the oscillators for each layer
    drud = Drude(An=A[i], Brn=Br[i])

    # Then we put them together inside a dielectric model
    model = DielectricConstantModel(e_inf=e_inf[i], oscillators=[drud])

    # We might want to calculate the RAT of the films
    out = calculate_rat([[300, model]], x)

    plt.figure(1)
    plt.plot(x / 1000, out['R'], 'b', label='R ' + label[i], ls=ls[i])
    plt.plot(x / 1000, out['A'], 'r', label='A ' + label[i], ls=ls[i])
    plt.plot(x / 1000, out['T'], 'g', label='T ' + label[i], ls=ls[i])

    # And also want to know the n and k data
    n = model.n_and_k(x)

    plt.figure(2)
    plt.plot(x / 1000, np.real(n), 'b', label='n ' + label[i], ls=ls[i])
    plt.plot(x / 1000, np.imag(n), 'g', label='k ' + label[i], ls=ls[i])

plt.figure(1)
plt.xlabel('Wavelength (µm)')
wavelengths = np.linspace(900, 1200, 30)

sim_fig6 = np.loadtxt('data/optos_fig6_sim.csv', delimiter=',')
sim_fig7 = np.loadtxt('data/optos_fig7_sim.csv', delimiter=',')
sim_fig8 = np.loadtxt('data/optos_fig8_sim.csv', delimiter=',')

rayflare_fig6 = np.loadtxt('fig6_rayflare.txt')
rayflare_fig7 = np.loadtxt('fig7_rayflare.txt')
rayflare_fig8 = np.loadtxt('fig8_rayflare.txt')

# planar
Si = material('Si_OPTOS')()
Air = material('Air')()
struc = OptiStack([Layer(si('200um'), Si)], substrate=Air)

RAT = calculate_rat(struc, wavelength=wavelengths, coherent=True)

fig = plt.figure()
plt.plot(sim_fig6[:,0], sim_fig6[:,1], '--', color=palhf[0], label= 'OPTOS - rear grating (a)')
plt.plot(wavelengths, rayflare_fig6, '-o', color=palhf[0], label='RayFlare - rear grating (a)', fillstyle='none')
plt.plot(sim_fig7[:,0], sim_fig7[:,1], '--', color=palhf[1],  label= 'OPTOS - front pyramids (b)')
plt.plot(wavelengths, rayflare_fig7, '-o', color=palhf[1],  label= 'RayFlare - front pyramids (b)', fillstyle='none')
plt.plot(sim_fig8[:,0], sim_fig8[:,1], '--', color=palhf[2],  label= 'OPTOS - grating + pyramids (c)')
plt.plot(wavelengths, rayflare_fig8, '-o', color=palhf[2],  label= 'RayFlare - grating + pyramids (c)', fillstyle='none')
plt.plot(wavelengths, RAT['A_per_layer'][1], '-k', label='Planar')
plt.legend(loc='lower left')
plt.xlabel('Wavelength (nm)')
plt.ylabel('Absorption in Si')
plt.xlim([900, 1200])
plt.ylim([0, 1])
fig.savefig('OPTOScomp.pdf', bbox_inches='tight', format='pdf')