def make_cell(self, x): #x[0]: total InGaP thickness #x[1]: total InGaAs thickness #x[2]: total SiGeSn thickness #x[3]: total Ge thickness #x[4]: InGaP n thickness #x[5]: InGaAs n thickness #x[6]: SiGeSn n thickness #x[7]: Ge n thickness e_charge = si('1eV') # materials SiGeSn = material('SiGeSn') GaAs = material('GaAs') InGaP = material('GaInP') Ge = material('Ge') MgF2 = material('MgF2')() Ta2O5 = material('410', nk_db=True)() AlInP = material("AlInP") window_material = AlInP(Al=0.52) GaInP_mobility_h = 0.03 # GaInP_lifetime_h = 1e-8 GaInP_D_h = GaInP_mobility_h * kb * 300 / e_charge GaInP_L_h = np.sqrt(GaInP_D_h * GaInP_lifetime_h) GaInP_mobility_e = 0.015 GaInP_lifetime_e = 1e-8 GaInP_D_e = GaInP_mobility_e * kb * 300 / e_charge GaInP_L_e = np.sqrt(GaInP_D_e * GaInP_lifetime_e) top_cell_n_material = InGaP(In=0.5, Nd=si("2e18cm-3"), hole_diffusion_length=GaInP_L_h, relative_permittivity=11.75, hole_mobility=GaInP_mobility_h) top_cell_p_material = InGaP(In=0.5, Na=si("2e17cm-3"), electron_diffusion_length=GaInP_L_e, relative_permittivity=11.75, electron_mobility=GaInP_mobility_e) # MID CELL - GaAs GaAs_mobility_h = 0.85 # GaAs_lifetime_h = 1e-8 GaAs_D_h = GaAs_mobility_h * kb * 300 / e_charge GaAs_L_h = np.sqrt(GaAs_D_h * GaAs_lifetime_h) GaAs_mobility_e = 0.08 GaAs_lifetime_e = 1e-8 GaAs_D_e = GaAs_mobility_e * kb * 300 / e_charge GaAs_L_e = np.sqrt(GaAs_D_e * GaAs_lifetime_e) mid_cell_n_material = GaAs(Nd=si("1e18cm-3"), hole_diffusion_length=GaAs_L_h, relative_permittivity=13.1, hole_mobility=GaAs_mobility_h) mid_cell_p_material = GaAs(Na=si("1e17cm-3"), electron_diffusion_length=GaAs_L_e, relative_permittivity=13.1, electron_mobility=GaAs_mobility_e) SiGeSn.band_gap = si('0.77eV') # from PL measurement SiGeSn_L_h = si('0.35um') SiGeSn_L_e = si('5um') SiGeSn_lifetime_e = 1e-6 SiGeSn_lifetime_h = 1e-6 SiGeSn_mobility_h = SiGeSn_L_h**2 * e_charge / (SiGeSn_lifetime_h * kb * 300) SiGeSn_mobility_e = SiGeSn_L_e**2 * e_charge / (SiGeSn_lifetime_e * kb * 300) pen_cell_n_material = SiGeSn(Nd=si("1e18cm-3"), hole_diffusion_length=SiGeSn_L_h, relative_permittivity=16, hole_mobility=SiGeSn_mobility_h) pen_cell_p_material = SiGeSn(Na=si("1e17cm-3"), electron_diffusion_length=SiGeSn_L_e, relative_permittivity=16, electron_mobility=SiGeSn_mobility_e) # Ge_mobility_h = 0.38 # Ge_lifetime_h = 1e-6 # Ge_D_h = Ge_mobility_h*kb*300/e_charge # Ge_L_h = np.sqrt(Ge_D_h*Ge_lifetime_h) Ge_L_h = si('500nm') Ge_mobility_h = Ge_L_h**2 * e_charge / (Ge_lifetime_h * kb * 300) Ge_mobility_e = 0.18 Ge_lifetime_e = 1e-6 Ge_D_e = Ge_mobility_e * kb * 300 / e_charge Ge_L_e = np.sqrt(Ge_D_e * Ge_lifetime_e) bot_cell_n_material = Ge(Nd=si("2e18cm-3"), hole_diffusion_length=Ge_L_h, relative_permittivity=16, hole_mobility=Ge_mobility_h) bot_cell_p_material = Ge(Na=si("1e17cm-3"), electron_diffusion_length=Ge_L_e, relative_permittivity=16, electron_mobility=Ge_mobility_e) # And, finally, we put everything together, adding also the surface recombination velocities. We also add some shading # due to the metallisation of the cell = 8%, and indicate it has an area of 0.7x0.7 mm2 (converted to m2) solar_cell = SolarCell( [ # Layer(si('110nm'), material = MgF2), Layer(si('55nm'), material = ZnS), Layer(si(self.ARC[0], 'nm'), material=MgF2), Layer(si(self.ARC[1], 'nm'), material=Ta2O5), Junction([ Layer( si(25, 'nm'), material=window_material, role='window'), Layer(si(x[4], 'nm'), material=top_cell_n_material, role='emitter'), Layer(si(x[0] - x[4], 'nm'), material=top_cell_p_material, role='base'), ], sn=1, sp=1, kind='DA'), Junction([ Layer(si(x[5], 'nm'), material=mid_cell_n_material, role='emitter'), Layer(si(x[1] - x[5], 'nm'), material=mid_cell_p_material, role='base'), ], sn=1, sp=1, kind='DA'), Junction([ Layer(si(x[6], 'nm'), material=pen_cell_n_material, role='emitter'), Layer(si(x[2] - x[6], 'nm'), material=pen_cell_p_material, role='base'), ], sn=1, sp=1, kind='DA'), Junction([ Layer(si(x[7], 'nm'), material=bot_cell_n_material, role='emitter'), Layer(si(x[3] - x[7], 'nm'), material=bot_cell_p_material, role='base'), ], sn=1, sp=1, kind='DA'), ], shading=0.0, substrate=bot_cell_n_material) return solar_cell
def layer2(b_mat): return Layer(width2, b_mat, role2, wkt_box)
try: num = self.junction_indices[junction] self[num].__dict__.update(kwargs) except IndexError: print( 'ERROR updating junction: The junction index must be {} or less.' .format(len(self.junction_indices))) def __call__(self, i): return self[self.junction_indices[i]] if __name__ == '__main__': window = material('AlGaAs')(T=298, Na=1e24, Al=0.8) stack = [Layer(width=si("50nm"), material=window), default_GaAs(298)] # stack = [default_GaAs(298)] my_cell = SolarCell(layers=stack) # # my_cell.append_multiple([default_GaAs(298), default_GaAs(298), default_GaAs(298)]) # print(my_cell) from solcore.poisson_drift_diffusion.DriftDiffusionUtilities import solve_pdd, default_photon_flux, \ default_wavelengths import matplotlib.pyplot as plt solve_pdd(my_cell, 'QE', vfin=1.2, vstep=0.05, light=True) QE = my_cell(0).qe
return result_band_edge, bands if __name__ == "__main__": from solcore import si, material from solcore.structure import Layer, Structure import matplotlib.pyplot as plt import numpy as np bulk = material("GaAs")(T=293) barrier = material("GaAsP")(T=293, P=0.1) bulk.strained = False barrier.strained = True top_layer = Layer(width=si("30nm"), material=bulk) inter = Layer(width=si("3nm"), material=bulk) barrier_layer = Layer(width=si("15nm"), material=barrier) bottom_layer = top_layer E = np.linspace(1.15, 1.5, 300) * q alfas = np.zeros((len(E), 6)) alfas[:, 0] = E / q alpha_params = { "well_width": si("7.2nm"), "theta": 0, "eps": 12.9 * vacuum_permittivity, "espace": E, "hwhm": si("6meV"),
def initialise_S(stack, size, orders): S = S4.New(((size[0], 0), (0, size[1])), orders) geom_list = [layer.geometry for layer in stack] geom_list.insert(0, {}) # incidence medium geom_list.append({}) # transmission medium ## Materials for the shapes need to be defined before you can do .SetRegion shape_mats = necessary_materials(geom_list) Layers = [] for x in shape_mats: Layers.append(Layer(0, x)) shape_mats_OS = OptiStack(Layers) for i1 in range(len(shape_mats) ): # create the materials needed for all the shapes in S4 S.SetMaterial('shape_mat_' + str(i1 + 1), 1) ## Make the layers stack_OS = OptiStack(stack) widths = stack_OS.get_widths() for i1 in range( len(widths) ): # create 'dummy' materials for base layers including incidence and transmission media S.SetMaterial( 'layer_' + str(i1 + 1), 1 ) # This is not strictly necessary but it means S.SetExcitationPlanewave # can be done outside the wavelength loop in calculate_rat_rcwa for i1 in range(len(widths)): # set base layers layer_name = 'layer_' + str(i1 + 1) if widths[i1] == float('Inf'): S.AddLayer( layer_name, 0, layer_name ) # Solcore4 has incidence and transmission media widths set to Inf; # in S4 they have zero width else: S.AddLayer(layer_name, widths[i1], layer_name) # keep base unit m, not nm geometry = geom_list[i1] if bool(geometry): for shape in geometry: mat_name = 'shape_mat_' + str( shape_mats.index(shape['mat']) + 1) if shape['type'] == 'circle': S.SetRegionCircle(layer_name, mat_name, shape['center'], shape['radius']) elif shape['type'] == 'ellipse': S.SetRegionEllipse(layer_name, mat_name, shape['center'], shape['angle'], shape['halfwidths']) elif shape['type'] == 'rectangle': S.SetRegionRectangle(layer_name, mat_name, shape['center'], shape['angle'], shape['halfwidths']) elif shape['type'] == 'polygon': S.SetRegionPolygon(layer_name, mat_name, shape['center'], shape['angle'], shape['vertices']) return S, stack_OS, shape_mats_OS
def test_43_TMM_absorption_profile(self): GaAs = material('GaAs')(T=300) my_structure = Structure([ Layer(si(3000, 'nm'), material=GaAs), Layer(si(300, 'um'), material=GaAs), ]) out = calculate_absorption_profile(my_structure, np.array([800]), z_limit=3000, steps_size=20) data = (0.00093920198054733134, 0.00091329190431268755, 0.00088809661793623094, 0.00086359640227330484, 0.00083977208217782804, 0.00081660501149482764, 0.0007940770584669893, 0.00077217059154379222, 0.00075086846558214263, 0.00073015400842769127, 0.00071001100786633451, 0.00069042369893569059, 0.00067137675158661923, 0.00065285525868512457, 0.00063484472434525627, 0.00061733105258387328, 0.00060030053628839001, 0.00058373984648887986, 0.00056763602192612503, 0.00055197645890746013, 0.00053674890144246557, 0.0005219414316507909, 0.00050754246043459913, 0.00049354071840833585, 0.00047992524707871981, 0.00046668539026805409, 0.0004538107857741483, 0.00044129135726031623, 0.00042911730636911071, 0.00041727910505361793, 0.00040576748812031177, 0.00039457344597762961, 0.00038368821758459675, 0.00037310328359397839, 0.00036281035968459497, 0.00035280139007758007, 0.00034306854123150837, 0.00033360419571145685, 0.00032440094622720458, 0.00031545158983589954, 0.00030674912230466134, 0.00029828673262870248, 0.00029005779770068137, 0.00028205587712711315, 0.00027427470818778237, 0.00026670820093421067, 0.00025935043342334711, 0.00025219564708274435, 0.00024523824220360293, 0.00023847277355814448, 0.00023189394613789383, 0.00022549661100952902, 0.0002192757612850577, 0.00021322652820316562, 0.00020734417731867015, 0.00020162410479709653, 0.00019606183381147725, 0.00019065301103855347, 0.0001853934032516379, 0.00018027889400747007, 0.00017530548042447405, 0.00017046927004989423, 0.00016576647781335848, 0.00016119342306448588, 0.00015674652669221659, 0.00015242230832361372, 0.00014821738359994165, 0.00014412846152789071, 0.00014015234190387394, 0.00013628591280938143, 0.00013252614817542982, 0.0001288701054142039, 0.00012531492311603331, 0.00012185781880990432, 0.00011849608678575335, 0.00011522709597683633, 0.00011204828790051968, 0.00010895717465587773, 0.00010595133697653208, 0.00010302842233720751, 0.00010018614311252307, 9.7422274786577231e-05, 9.4734654211925496e-05, 9.2121177916588886e-05, 8.9579800457766819e-05, 8.7108532820967001e-05, 8.470544086329888e-05, 8.2368643799712795e-05, 8.009631273099949e-05, 7.7886669212397987e-05, 7.573798386169243e-05, 7.3648575005706959e-05, 7.1616807364140996e-05, 6.9641090769713272e-05, 6.7719878923614451e-05, 6.5851668185292527e-05, 6.4034996395625842e-05, 6.2268441732560816e-05, 6.0550621598319883e-05, 5.8880191537308663e-05, 5.7255844183874585e-05, 5.5676308239094561e-05, 5.41403474757902e-05, 5.2646759770991723e-05, 5.1194376165094236e-05, 4.9782059946968693e-05, 4.8408705764312587e-05, 4.7073238758543685e-05, 4.5774613723559514e-05, 4.4511814287704784e-05, 4.3283852118305772e-05, 4.2089766148149713e-05, 4.0928621823303459e-05, 3.9799510371682887e-05, 3.8701548091800482e-05, 3.7633875661134488e-05, 3.6595657463578247e-05, 3.5586080935443529e-05, 3.4604355929505788e-05, 3.3649714096593824e-05, 3.2721408284239568e-05, 3.1818711951917646e-05, 3.0940918602416916e-05, 3.0087341228898868e-05, 2.9257311777210295e-05, 2.8450180623029266e-05, 2.7665316063435288e-05, 2.6902103822505617e-05, 2.6159946570550893e-05, 2.5438263456613905e-05, 2.4736489653865175e-05, 2.4054075917540213e-05, 2.3390488155071715e-05, 2.2745207008081107e-05, 2.211772744590139e-05, 2.1507558370314028e-05, 2.0914222231189692e-05, 2.0337254652732693e-05, 1.9776204070036316e-05, 1.9230631375664536e-05, 1.8700109575983667e-05, 1.8184223456974879e-05, 1.7682569259266036e-05, 1.7194754362128619e-05, 1.6720396976192213e-05, 1.6259125844636267e-05, 1.5810579952625165e-05, 1.5374408244759177e-05, 1.4950269350320186e-05, 1.4537831316097222e-05) for i in range(len(out['absorption'][0])): self.assertAlmostEqual(data[i], out['absorption'][0][i])
Air = material('Air')() # materials with constant n, zero k x = 1000 d_vectors = ((x, 0), (0, x)) area_fill_factor = 0.36 hw = np.sqrt(area_fill_factor) * 500 front_materials = [] back_materials = [ Layer(si('120nm'), Si, geometry=[{ 'type': 'rectangle', 'mat': Air, 'center': (x / 2, x / 2), 'halfwidths': (hw, hw), 'angle': 45 }]) ] # whether pyramids are upright or inverted is relative to front incidence. # so if the same etch is applied to both sides of a slab of silicon, one surface # will have 'upright' pyramids and the other side will have 'not upright' (inverted) # pyramids in the model front_surf = Interface('TMM', layers=front_materials, name='planar_front', coherent=True)
def GetEffectiveQW(self, calculate_absorption=True, wavelengths=None, periodic=True, filter_strength=0.0, blur=None, blurmode="left", mode='kp8x8_bulk', use_Adachi=False, alpha_params=None): """ Considers the device as a QW and solves its properties, including the modification of the bandeges due to strain, the efective mases and the absorption coefficient. The output is a list of layers made with materials with the effective properties after considering all these effects in addition to the quantum confinement. :param device: The device structure :param calculate_absorption: If absorption must be calculated :param WLsteps: wavelengths in which to calculate the absorption (input for np.linspace function) :param wavelengths: An array with the waveengths :param periodic: If it has to be assumed that the structure is perdiodic :param filter_strength: :param blur: :param blurmode: :param mode: :param use_Adachi: :param alpha_params: :return: A dictionary with the output of the Schrodinger solver. """ print('Solving QW properties...') self.solve(calculate_absorption=calculate_absorption, wavelengths=wavelengths, T=self.T, periodic=periodic, filter_strength=filter_strength, blur=blur, blurmode=blurmode, mode=mode, use_Adachi=use_Adachi, alpha_params=alpha_params) for i in range(len(self)): self[i].material.band_gap = self[i].eff_band_gap self[i].material.electron_affinity = self[i].eff_electron_affinity self[i].material.ni = np.sqrt( self[i].material.Nc * self[i].material.Nv * np.exp(-self[i].eff_band_gap / (kb * self.T))) # Finally, we re-build a list of layers with the effective properties new_QW = [] for i in range(len(self)): # We recover the composition and thickness layer_mat = self[i].material width = self[i].width # In the end, we convert the absorption coefficient in extinction coefficient kk = self[i].material.absorption * self.wl / 4 / np.pi layer_mat.k = interp1d(self.wl, kk, bounds_error=False, fill_value=(0, 0)) # And the radiative recombination parameter inter = lambda E: 1.0 / layer_mat.ni**2 * 2 * pi / ( h**3 * c**2) * layer_mat.n(1240e-9 / ( E / q))**2 * layer_mat.alphaE(E) * np.exp(-E / (kb * self.T )) * E**2 Br = -np.trapz(np.nan_to_num(inter(1240e-9 / self.wl * q)), 1240e-9 / self.wl * q) layer_mat.radiative_recombination = Br # And add the layer to the list of layers new_QW.append(Layer(width=width, material=layer_mat)) # As the QW might be actually a MQW, we repeat this as many times as needed new_QW = self.repeat * new_QW return new_QW
def test_quantum_mechanics_schrodinger(): """ Testing schrodinger equation solver """ bulk = material("GaAs")(T=293) barrier = material("GaAsP")(T=293, P=0.1) bulk.strained = False barrier.strained = True top_layer = Layer(width=si("30nm"), material=bulk) inter = Layer(width=si("3nm"), material=bulk) barrier_layer = Layer(width=si("15nm"), material=barrier) bottom_layer = top_layer E = np.linspace(1.15, 1.5, 300) * q alfas = np.zeros((len(E), 6)) alfas[:, 0] = E / q alpha_params = { "well_width": si("7.2nm"), "theta": 0, "eps": 12.9 * vacuum_permittivity, "espace": E, "hwhm": si("6meV"), "dimensionality": 0.16, "line_shape": "Gauss", } QW = material("InGaAs")(T=293, In=0.2) QW.strained = True well_layer = Layer(width=si("7.2nm"), material=QW) my_structure = Structure( [ top_layer, barrier_layer, inter, well_layer, inter, barrier_layer, inter, bottom_layer, ], substrate=bulk, ) band_edge, bands = QM.schrodinger( my_structure, quasiconfined=0, num_eigenvalues=20, alpha_params=alpha_params, calculate_absorption=True, ) for key in band_edge["E"]: for i in range(len(band_edge["E"][key])): band_edge["E"][key][i] = solcore.asUnit(band_edge["E"][key][i], "eV") * 1000 band_edge["E"][key][i] = round(band_edge["E"][key][i]) Ehh = np.all(np.equal(band_edge["E"]["Ehh"], my_energies["Ehh"])) Elh = np.all(np.equal(band_edge["E"]["Elh"], my_energies["Elh"])) Ee = np.all(np.equal(band_edge["E"]["Ee"], my_energies["Ee"])) idx = 100 out = [band_edge["alpha"][0][idx] / q, band_edge["alpha"][1][idx]] # Test over the energies assert Ehh and Elh and Ee # Test over the absorption coefficent at a given energy for i, data in enumerate(out): assert out[i] == approx(my_absorption[i], rel=1e-4)
def test_rcwa_polygon(): import numpy as np from solcore import material, si from solcore.structure import Layer, Structure from solcore.absorption_calculator.rigorous_coupled_wave import calculate_rat_rcwa, \ calculate_absorption_profile_rcwa T = 300 Air = material("Air")(T=T) TiO2 = material("TiO2", sopra=True)(T=T) # for the nanoparticles GaAs = material("GaAs")(T=T) th = 50 NP_layer_square = Layer( si(th, "nm"), Air, geometry=[{ "type": "rectangle", "mat": TiO2, "center": (200, 200), "halfwidths": [50, 50], "angle": 0 }], ) np_struct_square = Structure([NP_layer_square]) NP_layer_polygon = Layer( si(th, "nm"), Air, geometry=[{ "type": "polygon", "mat": TiO2, "center": (200, 200), "vertices": ((150, 150), (250, 150), (250, 250), (150, 250)), "angle": 0 }], ) np_struct_polygon = Structure([NP_layer_polygon]) wl = np.linspace(300, 1000, 10) step_size = 2 rat_np_sq = calculate_rat_rcwa(np_struct_square, size=((400, 0), (0, 400)), orders=10, wavelength=wl, substrate=GaAs, incidence=Air, pol=[1, 0]) A_output_sq = rat_np_sq['A_pol'] result_square = calculate_absorption_profile_rcwa(np_struct_square, size=((400, 0), (0, 400)), orders=10, wavelength=wl, rat_output_A=A_output_sq, parallel=True, steps_size=step_size, pol=[1, 0]) rat_np_pol = calculate_rat_rcwa(np_struct_polygon, size=((400, 0), (0, 400)), orders=10, wavelength=wl, substrate=GaAs, incidence=Air, pol=[1, 0]) A_output_pol = rat_np_pol['A_pol'] result_polygon = calculate_absorption_profile_rcwa( np_struct_polygon, size=((400, 0), (0, 400)), orders=10, wavelength=wl, rat_output_A=A_output_pol, parallel=True, steps_size=step_size, pol=[1, 0]) assert approx(A_output_sq == A_output_pol) assert approx(result_polygon["absorption"] == result_square["absorption"])
options.I_thresh = 1e-10 options.lookuptable_angles = 200 options.parallel = True options.c_azimuth = 0.05 options.theta_in = 80 * np.pi / 180 options.phi_in = 0 Si = material('Si')() GaAs = material('GaAs')() GaInP = material('GaInP')(In=0.5) Ag = material('Ag')() SiN = material('Si3N4')() Air = material('Air')() # stack based on doi:10.1038/s41563-018-0115-4 front_materials = [Layer(60e-9, SiN), Layer(50E-9, GaInP), Layer(50e-9, GaAs)] back_materials = [Layer(100E-9, GaInP), Layer(70e-9, GaAs)] # whether pyramids are upright or inverted is relative to front incidence. # so if the same etch is applied to both sides of a slab of silicon, one surface # will have 'upright' pyramids and the other side will have 'not upright' (inverted) # pyramids in the model front_surf = Interface('TMM', layers=front_materials, name='GaInP_GaAs', coherent=True) back_surf = Interface('TMM', layers=back_materials, name='Si_Ag', coherent=True)
def test_arbitrary_pol_rcwa(): import numpy as np from solcore import material, si from solcore.structure import Layer, Structure from solcore.absorption_calculator.rigorous_coupled_wave import calculate_rat_rcwa, \ calculate_absorption_profile_rcwa T = 300 Air = material("Air")(T=T) TiO2 = material("TiO2", sopra=True)(T=T) # for the nanoparticles GaAs = material("GaAs")(T=T) th = 50 NP_layer = Layer( si(th, "nm"), Air, geometry=[{ "type": "ellipse", "mat": TiO2, "center": (200, 200), "halfwidths": [100, 70], "angle": 40 }], ) np_struct = Structure([NP_layer]) wl = np.linspace(300, 1000, 10) step_size = 2 rat_np = calculate_rat_rcwa(np_struct, size=((400, 0), (0, 400)), orders=10, wavelength=wl, substrate=GaAs, incidence=Air, pol=[1, 0]) A_output = rat_np['A_pol'] result = calculate_absorption_profile_rcwa(np_struct, size=((400, 0), (0, 400)), orders=10, wavelength=wl, rat_output_A=A_output, parallel=True, steps_size=step_size, pol=[1, 0]) rat_np_s = calculate_rat_rcwa(np_struct, size=((400, 0), (0, 400)), orders=10, wavelength=wl, substrate=GaAs, incidence=Air, pol='s') A_output_s = rat_np_s['A_pol'] result_s = calculate_absorption_profile_rcwa(np_struct, size=((400, 0), (0, 400)), orders=10, wavelength=wl, rat_output_A=A_output_s, parallel=True, steps_size=step_size, pol='s') assert approx(A_output == A_output_s) assert approx(result["absorption"] == result_s["absorption"])
import solcore.poisson_drift_diffusion as PDD import numpy as np import matplotlib.pyplot as plt T = 298 wl = np.linspace(350, 1050, 601) * 1e-9 # First, we create the materials of the QW QWmat = material('InGaAs')(T=T, In=0.2, strained=True) Bmat = material('GaAsP')(T=T, P=0.1, strained=True) i_GaAs = material('GaAs')(T=T) # The QW is 7 nm wide, with GaAs interlayers 2 nm thick at each side and GaAsP barriers 10 nm thick. # The final device will have 30 of these QWs. QW = PDD.QWunit([Layer(width=10e-9, material=Bmat, role="barrier"), Layer(width=2e-9, material=i_GaAs, role="well"), Layer(width=7e-9, material=QWmat, role="well"), Layer(width=2e-9, material=i_GaAs, role="well"), Layer(width=10e-9, material=Bmat, role="barrier")], T=T, repeat=30, substrate=i_GaAs) # We solve the quantum properties of the QW, leaving the default values of all parameters QW_list = QW.GetEffectiveQW(wavelengths=wl) # Materials for the BOTTOM junction window_bottom = material('GaInP')(T=T, Nd=5e24, In=0.49) n_GaAs = material('GaAs')(T=T, Nd=1e24) p_GaAs = material('GaAs')(T=T, Na=8e22) bsf_bottom = material('GaInP')(T=T, Na=5e24, In=0.49) # If you want to test the code without QWs, to make ti a bit faster, comment the line with QW_list
def test_tmm_rcwa_structure_profile_comparison(pol, angle): import numpy as np from solcore import si, material from solcore.structure import Layer from solcore.solar_cell import SolarCell from solcore.optics.tmm import calculate_rat, calculate_absorption_profile, OptiStack from solcore.optics.rcwa import calculate_rat_rcwa, calculate_absorption_profile_rcwa InGaP = material('GaInP')(In=0.5) GaAs = material('GaAs')() Ge = material('Ge')() Ag = material('Ag')() Air = material('Air')() wavelengths = np.linspace(250, 1900, 8) size = ((100, 0), (0, 100)) solar_cell = SolarCell([ Layer(material=InGaP, width=si('400.5nm')), Layer(material=GaAs, width=si('500nm')), Layer(material=Ge, width=si('1000nm')) ], substrate=Ag) solar_cell_OS = OptiStack(solar_cell, no_back_reflection=False, substrate=solar_cell.substrate) rcwa_result = calculate_rat_rcwa(solar_cell, size, 2, wavelengths, Air, Ag, theta=angle, phi=angle, pol=pol, parallel=True) tmm_result = calculate_rat(solar_cell_OS, wavelengths, angle, pol, no_back_reflection=False) tmm_profile = calculate_absorption_profile(solar_cell_OS, wavelengths, no_back_reflection=False, angle=angle, pol=pol, RAT_out=tmm_result, steps_size=2) rcwa_profile = calculate_absorption_profile_rcwa( solar_cell, size, 2, wavelengths, rcwa_result['A_pol'], theta=angle, phi=angle, pol=pol, incidence=Air, substrate=solar_cell.substrate, parallel=True, steps_size=2) assert tmm_profile['position'] == approx(rcwa_profile['position'], abs=2e-6) assert tmm_profile['absorption'] == approx(rcwa_profile['absorption'], abs=2e-6)
wl = np.linspace(350, 1050, 601) * 1e-9 # First, we create the materials of the QW QWmat = material('InGaAs')(T=T, In=0.2, strained=True) Bmat = material('GaAsP')(T=T, P=0.1, strained=True) i_GaAs = material('GaAs')(T=T) # The QW is 7 nm wide, with GaAs interlayers 2 nm thick at each side and GaAsP barriers 10 nm thick. # The final device will have 30 of these QWs. QW = PDD.CreateDeviceStructure('QW', T=T, repeat=30, substrate=i_GaAs, layers=[ Layer(width=10e-9, material=Bmat, role="barrier"), Layer(width=2e-9, material=i_GaAs, role="interlayer"), Layer(width=7e-9, material=QWmat, role="well"), Layer(width=2e-9, material=i_GaAs, role="interlayer"), Layer(width=10e-9, material=Bmat, role="barrier") ])
ITO_back = material('ITO_lowdoping')() Perovskite = material('Perovskite_CsBr')() Ag = material('Ag_Jiang')() aSi_i = material('aSi_i')() aSi_p = material('aSi_p')() aSi_n = material('aSi_n')() LiF = material('LiF')() IZO = material('IZO')() C60 = material('C60')() # materials with constant n, zero k Spiro = [12e-9, np.array([0,1]), np.array([1.65, 1.65]), np.array([0,0])] SnO2 = [10e-9, np.array([0,1]), np.array([2, 2]), np.array([0,0])] # stack based on doi:10.1038/s41563-018-0115-4 front_materials = [Layer(100e-9, MgF2), Layer(110e-9, IZO), SnO2, Layer(15e-9, C60), Layer(1e-9, LiF), Layer(440e-9, Perovskite), Spiro, Layer(6.5e-9, aSi_n), Layer(6.5e-9, aSi_i)] back_materials = [Layer(6.5e-9, aSi_i), Layer(6.5e-9, aSi_p), Layer(240e-9, ITO_back)] # whether pyramids are upright or inverted is relative to front incidence. # so if the same etch is applied to both sides of a slab of silicon, one surface # will have 'upright' pyramids and the other side will have 'not upright' (inverted) # pyramids in the model surf = regular_pyramids(elevation_angle=55, upright=True) surf_back = regular_pyramids(elevation_angle=55, upright=False)
# materials with constant n, zero k x = 1000 front_materials = [] back_materials = [] # whether pyramids are upright or inverted is relative to front incidence. # so if the same etch is applied to both sides of a slab of silicon, one surface # will have 'upright' pyramids and the other side will have 'not upright' (inverted) # pyramids in the model surf = regular_pyramids(elevation_angle=55, upright=False) front_surf = Interface('RT_TMM', texture=surf, layers=[Layer(si('0.1nm'), Air)], name='inv_pyramids' + str(options['n_rays'])) back_surf = Interface('TMM', layers=[], name='planar_back' + str(options['n_rays'])) bulk_Si = BulkLayer(200e-6, Si, name='Si_bulk') # bulk thickness in m SC = Structure([front_surf, bulk_Si, back_surf], incidence=Air, transmission=Air) process_structure(SC, options) results = calculate_RAT(SC, options)
import numpy as np import matplotlib.pyplot as plt T = 298 wl = np.linspace(350, 1050, 301) * 1e-9 # First, we create the materials of the QW QWmat = material("InGaAs")(T=T, In=0.2, strained=True) Bmat = material("GaAsP")(T=T, P=0.1, strained=True) i_GaAs = material("GaAs")(T=T) # The QW is 7 nm wide, with GaAs interlayers 2 nm thick at each side and GaAsP barriers # 10 nm thick. The final device will have 30 of these QWs. QW = PDD.QWunit( [ Layer(width=10e-9, material=Bmat, role="barrier"), Layer(width=2e-9, material=i_GaAs, role="well"), Layer(width=7e-9, material=QWmat, role="well"), Layer(width=2e-9, material=i_GaAs, role="well"), Layer(width=10e-9, material=Bmat, role="barrier"), ], T=T, repeat=30, substrate=i_GaAs, ) # We solve the quantum properties of the QW, leaving the default values of all # parameters QW_list = QW.GetEffectiveQW(wavelengths=wl) # Materials for the BOTTOM junction
if __name__ == "__main__": from solcore import si, material from solcore.structure import Layer import matplotlib.pyplot as plt bulk = material("GaAs")(T=293) QW = material("InGaAs")(T=293, In=0.147) barrier = material("GaAsP")(T=293, P=0.1) bulk.strained = False QW.strained = True barrier.strained = True top_layer = Layer(width=si("50nm"), material=bulk) well_layer = Layer(width=si("7.2nm"), material=QW) barrier_layer = Layer(width=si("29nm"), material=barrier) bottom_layer = top_layer print("Here is a ASC_examples structure with no materials:") test_structure = assemble_qw_structure( repeats=3, well=well_layer, bulk_l_top=top_layer, bulk_l_bottom=bottom_layer, barrier=barrier_layer, ) test_structure.substrate = bulk
plt.xlabel("Wavelength (nm)") plt.ylabel("k") plt.show() # Compare performance as a back mirror on a GaAs 'cell' # Solid line: absorption in GaAs # Dashed line: absorption in Ag GaAs = material('GaAs')() colors = ['b', 'r', 'k', 'm', 'y'] plt.figure() for c, Ag_mat in enumerate([Ag_Joh, Ag_McP, Ag_Hag, Ag_Rak, Ag_Sol]): my_solar_cell = SolarCell([Layer(width=si('50nm'), material=GaAs)] + [Layer(width=si('100nm'), material=Ag_mat)]) solar_cell_solver(my_solar_cell, 'optics', opts) GaAs_positions = np.linspace( my_solar_cell[0].offset, my_solar_cell[0].offset + my_solar_cell[0].width, 1000) Ag_positions = np.linspace( my_solar_cell[1].offset, my_solar_cell[1].offset + my_solar_cell[1].width, 1000) GaAs_abs = np.trapz(my_solar_cell[0].diff_absorption(GaAs_positions), GaAs_positions) Ag_abs = np.trapz(my_solar_cell[1].diff_absorption(Ag_positions), Ag_positions) plt.plot(wl * 1e9, GaAs_abs, color=colors[c],
output['absorption'] = 0.5 * (data_s + data_p) return output if __name__ == '__main__': import matplotlib.pyplot as plt from solcore import material, si from solcore.structure import Layer, Structure GaAs = material('GaAs')(T=300) InGaAs = material('InGaAs')(T=300, In=0.1) my_structure = Structure([ Layer(si(3000, 'nm'), material=InGaAs), Layer(si(30, 'um'), material=GaAs), ]) wavelength = np.linspace(450, 1100, 300) out = calculate_rat(my_structure, wavelength, coherent=True, no_back_reflexion=False) # # # plt.plot(wavelength, out['R'], 'b', label='Reflexion') # plt.plot(wavelength, out['A'], 'r', label='Absorption') # plt.plot(wavelength, out['T'], 'g', label='Transmission') # plt.legend() # plt.show()
Air = material('Air')() # materials with constant n, zero k x = 1000 front_materials = [] back_materials = [] # whether pyramids are upright or inverted is relative to front incidence. # so if the same etch is applied to both sides of a slab of silicon, one surface # will have 'upright' pyramids and the other side will have 'not upright' (inverted) # pyramids in the model surf = regular_pyramids(elevation_angle=55, upright=False) front_surf = Interface('RT_TMM', texture = surf, layers=[Layer(si('0.01nm'), Air)], name = 'inv_pyramids' + str(options['nx']) + str(random_pos)) back_surf = Interface('TMM', layers=[], name = 'planar_back' + str(options['n_rays'])) bulk_Si = BulkLayer(201.8e-6, Si, name = 'Si_bulk') # bulk thickness in m SC = Structure([front_surf, bulk_Si, back_surf], incidence=Air, transmission=Air) process_structure(SC, options) results = calculate_RAT(SC, options) results_per_pass = results[1] R_per_pass = np.sum(results_per_pass['r'][0], 2) R_0 = R_per_pass[0]
p_GaAs = material('GaAs')(T=T, Na=si('4e18cm-3')) # for the GaAs cell emitter n_GaAs = material('GaAs')(T=T, Nd=si('2e17cm-3')) # for the GaAs cell base AlAs, GaAs = material('AlAs')(T=T), material('GaAs')(T=T) # for the DBR SiO2 = material('SiO2', sopra=True)(T=T) # for the spacer layer TiO2 = material('TiO2', sopra=True)(T=T) # for the nanoparticles # some parameters for the QE solver for mat in [n_GaAs, p_GaAs]: mat.hole_mobility, mat.electron_mobility, mat.permittivity = 3.4e-3, 5e-2, 9 n_GaAs.hole_diffusion_length, p_GaAs.electron_diffusion_length = si( "500nm"), si("5um") # Define the different parts of the structure we will use. For the GaAs junction, we use the depletion approximation GaAs_junction = [ Junction([ Layer(width=si('100nm'), material=p_GaAs, role="emitter"), Layer(width=si('400nm'), material=n_GaAs, role="base") ], T=T, kind='DA') ] # this creates 10 repetitions of the AlAs and GaAs layers, to make the DBR structure DBR = 10 * [ Layer(width=si("73nm"), material=AlAs), Layer(width=si("60nm"), material=GaAs) ] # The layer with nanoparticles NP_layer = [ Layer(si('50nm'),
options.theta_in = 0*np.pi/180 options.phi_in = 'all' options.only_incidence_angle = False Ge = material('Ge')() GaAs = material('GaAs')() GaInP = material('GaInP')(In=0.5) Ag = material('Ag')() SiN = material('Si3N4')() Air = material('Air')() Ta2O5 = material('410', nk_db=True)() MgF2 = material('203', nk_db=True)() SiGeSn = material('SiGeSn')() # stack based on doi:10.1038/s41563-018-0115-4 front_materials = [Layer(120e-9, MgF2), Layer(74e-9, Ta2O5), Layer(464e-9, GaInP), Layer(1682e-9, GaAs), Layer(1289e-9, SiGeSn)] back_materials = [Layer(100E-9, SiN)] # whether pyramids are upright or inverted is relative to front incidence. # so if the same etch is applied to both sides of a slab of silicon, one surface # will have 'upright' pyramids and the other side will have 'not upright' (inverted) # pyramids in the model ## TMM, matrix framework front_surf = Interface('TMM', layers=front_materials, name = 'GaInP_GaAs_SiGeSn_TMM', coherent=True) back_surf = Interface('TMM', layers=back_materials, name = 'SiN_Ag_TMM', coherent=True)
def layer1(qw_mat): return Layer(width1, qw_mat, role1, wkt_box, new_property='new_property')
options.project_name = 'testing' theta_intv, phi_intv, angle_vector = make_angle_vector(options['n_theta_bins'], options['phi_symmetry'], options['c_azimuth']) surf = regular_pyramids(elevation_angle=0, upright=True) # [texture, reverse] GaAs = material('GaAs')() Ge = material('Ge')() Si = material('Si')() Air = material('Air')() layers = [Layer(500e-9, GaAs), Layer(200e-9, Ge)] # optist = OptiStack(layers, False, Si, Air) # lookup table: size = ((500, 0), (0, 500)) results_front = RCWA(layers, size, orders, options, Air, Si, only_incidence_angle=False, front_or_rear='front', surf_name='testRCWA', detail_layer=False, save=True)
def layer3(i_gaas): return Layer(width3, i_gaas, role3, wkt_box)
all_materials.append(bot_cell_p_material) # We add some other properties to the materials, assumed the same in all cases, for simplicity. # If different, we should have added them above in the definition of the materials. for mat in all_materials: mat.hole_mobility = 5e-2 mat.electron_mobility = 3.4e-3 mat.hole_mobility = 3.4e-3 mat.electron_mobility = 5e-2 mat.permittivity = 9 # And, finally, we put everything together, adding also the surface recombination velocities. We also add some shading # due to the metallisation of the cell = 8%, and indicate it has an area of 0.7x0.7 mm2 (converted to m2) solar_cell = SolarCell([ Junction([ Layer(si("25nm"), material=window_material, role='window'), Layer(si("100nm"), material=top_cell_n_material, role='emitter'), Layer(si("600nm"), material=top_cell_p_material, role='base'), ], sn=1, sp=1, kind='DA'), Junction([ Layer(si("200nm"), material=mid_cell_n_material, role='emitter'), Layer(si("3000nm"), material=mid_cell_p_material, role='base'), ], sn=1, sp=1, kind='DA'), Junction([ Layer(si("400nm"), material=bot_cell_n_material, role='emitter'),
def run_solar_cell_model(task, model): """ :param model: :return: a Solar cell object """ # First, we take care of the options # ---------------------------------- options = copy.copy(model['Global']) options.update(model['Electrical']) options.update(model['Optical']) T = float(options['T']) # We have to validate the options and check they have the correct format and type array_based_options = ['voltages', 'internal_voltages', 'position', 'wavelength'] float_options = ['T', 'T_ambient'] bool_options = ['mpp', 'light_iv', 'radiative_coupling'] try: for p in options: # For the options that should be arrays, we create the arrays. The wavelength need ot be converted to meters if p in array_based_options: c = options[p].split(', ') ini = float(c[0]) end = float(c[1]) num = int(c[2]) options[p] = np.linspace(ini, end, num) * 1e-9 ** (p == 'wavelength') # For the options that need to be floats elif p in float_options: options[p] = float(options[p]) elif p in bool_options: options[p] = options[p].lower() in ['true', 'yes', 't', 1] except Exception as err: print('ERROR parsing the solver options: Option format not recognised') print(err) raise # Now we create the layers and junctions to the solar cell object # --------------------------------------------------------------- sc_data = model['Solar cell'] all_layers = [] all_materials = [] for i in sc_data['structure']: current_element = sc_data[i[0]] # First the individual layers if 'Layer' in current_element['type']: layer_properties = {} width = current_element['width'] * 1e-9 # Set the composition and get the properties, converting them to the correct units for key in current_element['options']: if key in ['element', 'x']: layer_properties[current_element['options']['element']] = current_element['options']['x'] else: layer_properties[key] = current_element['options'][key] * conversion[key] all_materials.append(material(current_element['material'])(T=T, **layer_properties)) all_layers.append(Layer(width, all_materials[-1], role=current_element['name'])) continue # Unless it is an individual layer, we have junctions properties = {} for p in properties_junctions[current_element['type']]: properties[p] = default_junction_properties[p] properties.update(**current_element['options']) kind = current_element['type'].split('-')[-1] if 'TJ' in current_element['type']: new_junction = TunnelJunction(name=current_element['name'], kind=kind, T=T, **properties) else: new_junction = Junction(name=current_element['name'], kind=kind, T=T, **properties) # Now we add the layers, if any for l in i[1:]: current_child = sc_data[l] width = current_child['width'] * 1e-9 layer_properties = {} # Set the composition and get the properties, converting them to the correct units for key in current_child['options']: if key in ['element', 'x']: layer_properties[current_child['options']['element']] = current_child['options']['x'] else: layer_properties[key] = current_child['options'][key] * conversion[key] all_materials.append(material(current_child['material'])(T=T, **layer_properties)) new_junction.append(Layer(width, all_materials[-1], role=current_child['name'])) all_layers.append(new_junction) # Now we have to create the solar cell # ------------------------------------ reflectivity = create_reflectivity(sc_data['reflectivity']) if sc_data['reflectivity'] != '' else None try: substrate = material(sc_data['substrate'])(T=T, **{sc_data['element']: sc_data['composition']}) except KeyError: substrate = material(sc_data['substrate'])(T=T) sc = SolarCell(layers=all_layers, name=sc_data['name'], substrate=substrate, T=T, cell_area=sc_data['size'], shading=sc_data['shading'], R_series=sc_data['r_series'], reflectivity=reflectivity) # With all said and done, we can run the solver # --------------------------------------------- solar_cell_solver(sc, task, user_options=options) print('Done!') return sc
all_materials.append(bot_buffer_material) all_materials.append(bot_nucleation_material) all_materials.append(bot_cell_n_material) all_materials.append(bot_cell_p_material) # We add some other properties to the materials, assumed the same in all cases, for simplicity. # If different, we should have added them above in the definition of the materials. for mat in all_materials: mat.hole_mobility = 3.4e-3 mat.electron_mobility = 5e-2 ARC = [Layer(si('80nm'), material = ARC1), Layer(si('33nm'), material = ARC2)] top_junction = [Junction([Layer(si("18nm"), material=top_window_material, role='window'), Layer(si("100nm"), material=top_cell_n_material, role='emitter'), Layer(si("891.248nm"), material=top_cell_p_material, role='base'), Layer(si("111.445nm"), material = top_cell_TJ_material, role = 'TJ') ], sn=1, sp=1, kind='DA')] middle_junction = [Junction([Layer(si("18nm"), material=mid_window_material, role='window'), Layer(si("100nm"), material=mid_cell_n_material, role='emitter'), Layer(si("1632.091nm"), material=mid_cell_p_material, role='base'), Layer(si("10nm"), material = mid_BSF_material, role = 'BSF'), Layer(si("91.084nm"), material=mid_cell_TJ_material, role='TJ') ], sn=1, sp=1, kind='DA')] DBRa = 16 * [Layer(width=si("62.638nm"), material=DBR1), Layer(width=si("71.980nm"), material=DBR2)] DBRb = 16 * [Layer(width=si("68.919nm"), material=DBR1), Layer(width=si("78.725nm"), material=DBR2)] DBRc = 16 * [Layer(width=si("75.838nm"), material=DBR1), Layer(width=si("86.805nm"), material=DBR2)]