def test_BL_correction(): wl = np.linspace(800, 950, 4) * 1e-9 GaAs = material("GaAs")() thick_cell = SolarCell([Layer(material=GaAs, width=si("20um"))]) opts = State() opts.position = None prepare_solar_cell(thick_cell, opts) position = np.arange(0, thick_cell.width, 1e-9) opts.position = position opts.recalculate_absorption = True opts.no_back_reflexion = False opts.BL_correction = False opts.wavelength = wl solve_tmm(thick_cell, opts) no_corr = thick_cell.absorbed opts.BL_correction = True solve_tmm(thick_cell, opts) with_corr = thick_cell.absorbed assert with_corr == approx( np.array([6.71457872e-01, 6.75496354e-01, 2.09738887e-01, 0])) assert no_corr == approx( np.array([6.71457872e-01, 6.75496071e-01, 2.82306407e-01, 0]))
def test_tmm_rcwa_structure_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, OptiStack from solcore.optics.rcwa import calculate_rat_rcwa InGaP = material('GaInP')(In=0.5) GaAs = material('GaAs')() Ge = material('Ge')() Ag = material('Ag')() Air = material('Air')() Al2O3 = material('Al2O3')() wavelengths = np.linspace(250, 1900, 500) size = ((100, 0), (0, 100)) ARC = [Layer(si('80nm'), Al2O3)] solar_cell = SolarCell(ARC + [ Layer(material=InGaP, width=si('400nm')), Layer(material=GaAs, width=si('4000nm')), Layer(material=Ge, width=si('3000nm')) ], 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) assert tmm_result['A_per_layer'][1:-1] == approx( rcwa_result['A_per_layer'].T) assert tmm_result['R'] == approx(rcwa_result['R']) assert tmm_result['T'] == approx(rcwa_result['T']) assert np.sum(tmm_result['A_per_layer'][1:-1].T, 1) + tmm_result['R'] + tmm_result['T'] == approx(1) assert np.sum(rcwa_result['A_per_layer'], 1) + rcwa_result['R'] + rcwa_result['T'] == approx(1)
def plot_R(obj, x, icol): cols = sns.cubehelix_palette(len(use_orders), start=.5, rot=-.9, reverse=True) GaAs = material('GaAs_WVASE')() Air = material('Air')() InAlP = material('InAlP_WVASE')() InGaP = material('InGaP_WVASE')() Ag = material('Ag_Jiang')() # MgF2 = material('MgF2')() # Ta2O5 = material('410', nk_db=True)() SiN = material('SiN_SE')() R_data = np.loadtxt('Talbot_precursor_R.csv', delimiter=',') grating1 = [Layer(si(x[2] * x[1], 'nm'), SiN)] grating2 = [ Layer(si((1 - x[1]) * x[2], 'nm'), SiN, geometry=[{ 'type': 'circle', 'mat': Ag, 'center': (0, 0), 'radius': x[3], 'angle': 0 }]) ] solar_cell = SolarCell([ Layer(material=GaAs, width=si('25nm')), Layer(material=InGaP, width=si('19nm')), Layer(material=GaAs, width=si(x[0], 'nm')), Layer(material=InAlP, width=si('18nm')) ] + grating1 + grating2, substrate=Ag) S4_setup = rcwa_structure(solar_cell, obj.size, obj.orders, obj.options, Air, Ag) RAT = S4_setup.calculate() total_A = np.sum(RAT['A_layer'], axis=1) # plt.figure() plt.plot(S4_setup.wavelengths * 1e9, RAT['R'], label=str(obj.orders), color=cols[icol]) return S4_setup.wavelengths * 1e9, interp1d(R_data[:, 0], R_data[:, 1])( S4_setup.wavelengths * 1e9)
def test_substrate_presence_profile(): wavelength = np.linspace(300, 800, 3) * 1e-9 GaAs = material("GaAs")(T=300) my_structure = SolarCell([Layer(si(700, "nm"), material=GaAs)], substrate=GaAs) solar_cell_solver( my_structure, "optics", user_options={ "wavelength": wavelength, "optics_method": "TMM", "no_back_reflection": False, }, ) z_pos = np.linspace(0, my_structure.width, 10) profile_subs = my_structure[0].absorbed(z_pos) my_structure = SolarCell([Layer(si(700, "nm"), material=GaAs)]) solar_cell_solver( my_structure, "optics", user_options={ "wavelength": wavelength, "optics_method": "TMM", "no_back_reflection": False, }, ) profile_nosubs = my_structure[0].absorbed(z_pos) profile = np.vstack((profile_subs, profile_nosubs)) data_path = Path( __file__).parent / "data" / "substrate_presence_profile.csv" expected = np.loadtxt(data_path, delimiter=",") assert profile.shape == expected.shape assert profile == approx(expected)
def junction(nd_top, na_top, nd_bottom, na_bottom): from solcore.structure import Junction, Layer from solcore import si, material from solcore.solar_cell import SolarCell from solcore.constants import vacuum_permittivity from solcore.solar_cell_solver import prepare_solar_cell from solcore.optics import solve_beer_lambert Lp = np.power(10, np.random.uniform(-8, -6)) Ln = np.power(10, np.random.uniform(-8, -6)) AlInP = material("AlInP") InGaP = material("GaInP") window_material = AlInP(Al=0.52) top_cell_n_material = InGaP( In=0.48, Nd=nd_top, Na=na_top, hole_diffusion_length=Lp, electron_diffusion_length=Ln, ) top_cell_p_material = InGaP( In=0.48, Nd=nd_bottom, Na=na_bottom, hole_diffusion_length=Lp, electron_diffusion_length=Ln, ) rel_perm = np.random.uniform(1, 20) for mat in [top_cell_n_material, top_cell_p_material]: mat.permittivity = rel_perm * vacuum_permittivity n_width = np.random.uniform(500, 1000) * 1e-9 p_width = np.random.uniform(3000, 5000) * 1e-9 test_junc = SolarCell([ Junction( [ Layer(si("25nm"), material=window_material, role="window"), Layer(n_width, material=top_cell_n_material, role="emitter"), Layer(p_width, material=top_cell_p_material, role="base"), ], sn=1, sp=1, kind="DA", ) ]) options = da_options() options.light_source = da_light_source() prepare_solar_cell(test_junc, options) solve_beer_lambert(test_junc, options) return test_junc, options
def test_substrate_presence_A(): wavelength = np.linspace(300, 800, 3) * 1e-9 GaAs = material("GaAs")(T=300) my_structure = SolarCell([Layer(si(700, "nm"), material=GaAs)], substrate=GaAs) solar_cell_solver( my_structure, "optics", user_options={ "wavelength": wavelength, "optics_method": "TMM", "no_back_reflexion": False, }, ) z_pos = np.linspace(0, my_structure.width, 10) A_subs = my_structure[0].layer_absorption my_structure = SolarCell([Layer(si(700, "nm"), material=GaAs)]) solar_cell_solver( my_structure, "optics", user_options={ "wavelength": wavelength, "optics_method": "TMM", "no_back_reflexion": False, }, ) A_nosubs = my_structure[0].layer_absorption A = np.vstack((A_subs, A_nosubs)) A_data = np.array([[0.56610281, 0.62692985, 0.41923175], [0.56610281, 0.62711355, 0.37837737]]) assert all([d == approx(o) for d, o in zip(A, A_data)])
def test_light_iv(AlGaAs, light_source): import numpy as np from solcore.solar_cell import SolarCell, default_GaAs from solcore import material from solcore.solar_cell_solver import solar_cell_solver expected = np.array([ 142.68025180227374, 2.519346556870366, 0.9169672186977382, 329.61395441947565, 2.347826086956522, 140.3911287342211, 0.3294918264376029, ]) T = AlGaAs.T Vin = np.linspace(-2, 2.61, 201) V = np.linspace(0, 2.6, 300) substrate = material("GaAs")(T=T) my_solar_cell = SolarCell([AlGaAs, default_GaAs(T)], T=T, R_series=0, substrate=substrate) solar_cell_solver( my_solar_cell, "iv", user_options={ "T_ambient": T, "db_mode": "boltzmann", "voltages": V, "light_iv": True, "wavelength": light_source.x, "optics_method": "BL", "mpp": True, "internal_voltages": Vin, "light_source": light_source, }, ) output = [ my_solar_cell.iv.Isc, my_solar_cell.iv.Voc, my_solar_cell.iv.FF, my_solar_cell.iv.Pmpp, my_solar_cell.iv.Vmpp, my_solar_cell.iv.Impp, my_solar_cell.iv.Eta, ] assert np.array(output) == approx(expected, rel=1e-2)
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 test_light_iv(): answer = [ 142.68025180227374, 2.519346556870366, 0.9169672186977382, 329.61395441947565, 2.347826086956522, 140.3911287342211, 0.3294918264376029, ] with tempfile.TemporaryDirectory(prefix="tmp", suffix="_sc3TESTS") as working_directory: filename = os.path.join(working_directory, "solcore_log.txt") PDD.log(filename) my_solar_cell = SolarCell([AlGaAs(T), default_GaAs(T)], T=T, R_series=0, substrate=substrate) solar_cell_solver( my_solar_cell, "iv", user_options={ "T_ambient": T, "db_mode": "boltzmann", "voltages": V, "light_iv": True, "wavelength": wl, "optics_method": "BL", "mpp": True, "internal_voltages": Vin, "light_source": light_source, }, ) output = [ my_solar_cell.iv.Isc, my_solar_cell.iv.Voc, my_solar_cell.iv.FF, my_solar_cell.iv.Pmpp, my_solar_cell.iv.Vmpp, my_solar_cell.iv.Impp, my_solar_cell.iv.Eta, ] for i in range(len(output)): assert output[i] == approx(answer[i])
def test_quantum_efficiency(AlGaAs, light_source): import numpy as np from solcore.solar_cell import SolarCell, default_GaAs from solcore import material from solcore.solar_cell_solver import solar_cell_solver expected = np.array([ 0.9866334968497021, 2.1512408472022467e-14, 0.9779769012349702, 0.03506561338387434, ]) T = AlGaAs.T Vin = np.linspace(-2, 2.61, 201) V = np.linspace(0, 2.6, 300) substrate = material("GaAs")(T=T) my_solar_cell = SolarCell([AlGaAs, default_GaAs(T)], T=T, R_series=0, substrate=substrate) solar_cell_solver( my_solar_cell, "qe", user_options={ "T_ambient": T, "db_mode": "boltzmann", "voltages": V, "light_iv": True, "wavelength": light_source.x, "optics_method": "BL", "mpp": True, "internal_voltages": Vin, "light_source": light_source, }, ) output = [ my_solar_cell[0].eqe(500e-9), my_solar_cell[0].eqe(800e-9), my_solar_cell[1].eqe(700e-9), my_solar_cell[1].eqe(900e-9), ] assert np.array(output) == approx(expected, abs=1e-3)
def evaluate(self, x): GaAs = material('GaAs_WVASE')() Air = material('Air')() InAlP = material('InAlP_WVASE')() InGaP = material('InGaP_WVASE')() Ag = material('Ag_Jiang')() # MgF2 = material('MgF2')() # Ta2O5 = material('410', nk_db=True)() SiN = material('SiN_SE')() # print('SiN thickness', x[2]*x[1], 'Ag th', (1-x[1])*x[2]) grating1 = [Layer(si(x[2] * x[1], 'nm'), SiN)] grating2 = [ Layer(si((1 - x[1]) * x[2], 'nm'), SiN, geometry=[{ 'type': 'circle', 'mat': Ag, 'center': (0, 0), 'radius': x[3], 'angle': 0 }]) ] solar_cell = SolarCell([ Layer(material=InGaP, width=si('19nm')), Layer(material=GaAs, width=si(x[0], 'nm')), Layer(material=InAlP, width=si('18nm')) ] + grating1 + grating2, substrate=Ag) S4_setup = rcwa_structure(solar_cell, self.size, self.orders, self.options, Air, Ag) RAT = S4_setup.calculate() EQE_sim = 0.9 * 100 * (RAT['A_layer'][:, 0] + RAT['A_layer'][:, 1]) # least squares? residual = np.sum((EQE_sim - self.EQE_meas)**2) return residual
def test_quantum_efficiency(): answer = [ 0.9866334968497021, 2.1512408472022467e-14, 0.9779769012349702, 0.03506561338387434, ] with tempfile.TemporaryDirectory(prefix="tmp", suffix="_sc3TESTS") as working_directory: filename = os.path.join(working_directory, "solcore_log.txt") PDD.log(filename) my_solar_cell = SolarCell([AlGaAs(T), default_GaAs(T)], T=T, R_series=0, substrate=substrate) solar_cell_solver( my_solar_cell, "qe", user_options={ "T_ambient": T, "db_mode": "boltzmann", "voltages": V, "light_iv": True, "wavelength": wl, "optics_method": "BL", "mpp": True, "internal_voltages": Vin, "light_source": light_source, }, ) output = [ my_solar_cell[0].eqe(500e-9), my_solar_cell[0].eqe(800e-9), my_solar_cell[1].eqe(700e-9), my_solar_cell[1].eqe(900e-9), ] for i in range(len(output)): assert output[i] == approx(answer[i])
def test_inc_coh_tmm(): GaInP = material("GaInP")(In=0.5) GaAs = material("GaAs")() Ge = material("Ge")() optical_struct = SolarCell([ Layer(material=GaInP, width=si("5000nm")), Layer(material=GaAs, width=si("200nm")), Layer(material=GaAs, width=si("5um")), Layer(material=Ge, width=si("50um")), ]) wl = np.linspace(400, 1200, 5) * 1e-9 options = State() options.wavelength = wl options.optics_method = "TMM" options.no_back_reflection = False options.BL_correction = True options.recalculate_absorption = True c_list = [ ["c", "c", "c", "c"], ["c", "c", "c", "i"], ["c", "i", "i", "c"], ["i", "i", "i", "i"], ] results = [] for i1, cl in enumerate(c_list): options.coherency_list = cl solar_cell_solver(optical_struct, "optics", options) results.append(optical_struct.absorbed) A_calc = np.stack(results) A_data = np.array( [[0.5742503, 0.67956899, 0.73481184, 0.725372, 0.76792856], [0.5742503, 0.67956899, 0.73481184, 0.725372, 0.76792856], [0.5742503, 0.67956899, 0.73474943, 0.70493469, 0.70361194], [0.5742503, 0.67956899, 0.70927724, 0.71509221, 0.71592772]]) assert A_calc == approx(A_data)
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 prepare_test_cell(): from solcore import material, si from solcore.structure import Junction, Layer, TunnelJunction from solcore.solar_cell import SolarCell GaAs = material("GaAs")() MgF2 = material("MgF2")() TiO2 = material("TiO2")() Ge = material("Ge")() widths = np.random.rand(9) * 200 solar_cell = SolarCell([ Layer(si(widths[0], "nm"), material=MgF2), Layer(si(widths[1], "nm"), material=TiO2), Junction( [ Layer(si(widths[2], "nm"), material=GaAs, role="window"), Layer(si(widths[3], "nm"), material=GaAs, role="emitter"), Layer(si(widths[4], "nm"), material=GaAs, role="base"), ], kind="DA", ), TunnelJunction([ Layer(si(widths[5], "nm"), material=GaAs), Layer(si(widths[6], "nm"), material=GaAs), ]), Junction( [ Layer(si(widths[7], "nm"), material=Ge, role="emitter"), Layer(si(widths[8], "nm"), material=Ge, role="base"), ], kind="PDD", ), ]) return solar_cell, widths
def test_92_light_iv(self): answer = [ 142.68025180227374, 2.519346556870366, 0.9169672186977382, 329.61395441947565, 2.347826086956522, 140.3911287342211, 0.3294918264376029 ] with tempfile.TemporaryDirectory( prefix="tmp", suffix="_sc3TESTS") as working_directory: filename = os.path.join(working_directory, 'solcore_log.txt') PDD.log(filename) my_solar_cell = SolarCell([AlGaAs(T), default_GaAs(T)], T=T, R_series=0, substrate=substrate) solar_cell_solver(my_solar_cell, 'iv', user_options={ 'T_ambient': T, 'db_mode': 'boltzmann', 'voltages': V, 'light_iv': True, 'wavelength': wl, 'optics_method': 'BL', 'mpp': True, 'internal_voltages': Vin, 'light_source': light_source }) output = [ my_solar_cell.iv.Isc, my_solar_cell.iv.Voc, my_solar_cell.iv.FF, my_solar_cell.iv.Pmpp, my_solar_cell.iv.Vmpp, my_solar_cell.iv.Impp, my_solar_cell.iv.Eta ] for i in range(len(output)): self.assertAlmostEqual(output[i], answer[i])
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) my_solar_cell = SolarCell([AlGaAs(T), default_GaAs(T)], T=T, R_series=0, substrate=substrate) solar_cell_solver(my_solar_cell, 'iv', user_options={ 'T_ambient': T, 'db_mode': 'boltzmann', 'voltages': V, 'light_iv': True, 'wavelength': wl, 'optics_method': 'BL', 'mpp': True, 'internal_voltages': Vin, 'light_source': light_source }) output = [ my_solar_cell.iv.Isc, my_solar_cell.iv.Voc, my_solar_cell.iv.FF, my_solar_cell.iv.Pmpp, my_solar_cell.iv.Vmpp, my_solar_cell.iv.Impp, my_solar_cell.iv.Eta ] for i in range(len(output)): self.assertAlmostEqual(output[i], answer[i])
def test_93_qe(self): answer = [ 0.9831923128532823, 1.315965183418519e-13, 0.9672990699170962, 0.032767290395462376 ] with tempfile.TemporaryDirectory( prefix="tmp", suffix="_sc3TESTS") as working_directory: filename = os.path.join(working_directory, 'solcore_log.txt') PDD.log(filename) my_solar_cell = SolarCell([AlGaAs(T), default_GaAs(T)], T=T, R_series=0, substrate=substrate) solar_cell_solver(my_solar_cell, 'qe', user_options={ 'T_ambient': T, 'db_mode': 'boltzmann', 'voltages': V, 'light_iv': True, 'wavelength': wl, 'optics_method': 'BL', 'mpp': True, 'internal_voltages': Vin, 'light_source': light_source }) output = [ my_solar_cell[0].eqe(500e-9), my_solar_cell[0].eqe(800e-9), my_solar_cell[1].eqe(700e-9), my_solar_cell[1].eqe(900e-9) ] for i in range(len(output)): self.assertAlmostEqual(output[i], answer[i])
def test_93_qe(self): answer = [ 0.9866334968497021, 2.1512408472022467e-14, 0.9779769012349702, 0.03506561338387434 ] with tempfile.TemporaryDirectory( prefix="tmp", suffix="_sc3TESTS") as working_directory: filename = os.path.join(working_directory, 'solcore_log.txt') PDD.log(filename) my_solar_cell = SolarCell([AlGaAs(T), default_GaAs(T)], T=T, R_series=0, substrate=substrate) solar_cell_solver(my_solar_cell, 'qe', user_options={ 'T_ambient': T, 'db_mode': 'boltzmann', 'voltages': V, 'light_iv': True, 'wavelength': wl, 'optics_method': 'BL', 'mpp': True, 'internal_voltages': Vin, 'light_source': light_source }) output = [ my_solar_cell[0].eqe(500e-9), my_solar_cell[0].eqe(800e-9), my_solar_cell[1].eqe(700e-9), my_solar_cell[1].eqe(900e-9) ] for i in range(len(output)): self.assertAlmostEqual(output[i], answer[i])
# the 4* here makes the two layers given repeat 4 times (so 8 layers total) bottom_junction = [ Junction([ Layer(si("405.048nm"), material=bot_buffer_material, role='window'), Layer(si("14.369nm"), material=bot_nucleation_material, role='window'), Layer(si("200nm"), material=bot_cell_n_material, role='emitter'), Layer(si("29800nm"), material=bot_cell_p_material, role='base') ], sn=1, sp=1, kind='DA') ] # And, finally, we put everything together, adding also the surface recombination velocities sn and sp. # setting kind = 'DA' in the Junction definition tells the electrical solver later to use the depletion approximation optical_struct = SolarCell(ARC + top_junction + middle_junction + DBRa + DBRb + DBRc + bottom_junction, shading=0.05) wl = np.linspace(250, 1700, 400) * 1e-9 options = State() options.wavelength = wl options.optics_method = 'TMM' options.no_back_reflection = False options.pol = 'p' options.BL_correction = True options.coherency_list = 111 * ['c'] options.theta = 30 solar_cell_solver(optical_struct, 'qe', options) plt.figure()
relative_permittivity=11.68) p_material = Si(T=T, Na=si(1e16, "cm-3"), electron_diffusion_length=si("150um"), hole_mobility=400e-4, relative_permittivity=11.68) ARC_width = si("100nm") n_material_width = si("500nm") p_material_width = si("50um") solar_cell = SolarCell([ Layer(width=ARC_width, material=SiO2), Junction([ Layer(width=n_material_width, material=n_material, role='emitter'), Layer(width=p_material_width, material=p_material, role='base'), ], sn=1, sp=1, kind='DA'), ]) total_width = ARC_width + n_material_width + p_material_width options = default_options options.optics_method = "TMM" options.wavelength = wavelengths_optics # options.position = np.linspace(0, total_width, 100000) options.light_iv = True V = np.linspace(0, 1.2, 200) solar_cell_solver(solar_cell, 'iv', options)
light_source = LightSource(source_type='standard', version='AM1.5g', x=wl, output_units='photon_flux_per_m', concentration=1) color = ['b', 'g', 'r'] label = ['Top', 'Mid', 'Bot'] fig, ax = plt.subplots(1, 2, sharey='all', figsize=(7, 4.5)) for k, rad in enumerate([False, True]): # Input data for the 2D kind of junction db_junction = Junction(kind='2D', T=T, reff=0.3, jref=300, Eg=0.66, A=1, R_shunt=np.inf, n=3.5) db_junction2 = Junction(kind='2D', T=T, reff=1, jref=300, Eg=1.4, A=1, R_shunt=np.inf, n=3.5) db_junction3 = Junction(kind='2D', T=T, reff=1, jref=300, Eg=1.8, A=1, R_shunt=np.inf, n=3.5) my_solar_cell = SolarCell([db_junction3, db_junction2, db_junction], T=T, R_series=0) solar_cell_solver(my_solar_cell, 'iv', user_options={'T_ambient': T, 'voltages': V, 'light_iv': True, 'wavelength': wl, 'light_source': light_source, 'radiative_coupling': rad, 'mpp': True, 'internal_voltages': Vin}) # This is the total junction IV ax[k].plot(my_solar_cell.iv['IV'][0], my_solar_cell.iv['IV'][1], marker='o', color=colours("Black"), ls='-', markerfacecolor='none', markeredgecolor=colours("Black")) # This is the junciton IV when it is in the MJ device, including coupling if it is enabled. for i, data in enumerate(my_solar_cell.iv['junction IV']): ax[k].plot(data[0], data[1], color[i] + '--', linewidth=2) # This is the junction IV as if it were an isolated device and therefore not affected by coupling or current limiting.
# The layer with nanoparticles NP_layer = [ Layer(si('50nm'), Air, geometry=[{ 'type': 'circle', 'mat': TiO2, 'center': (200, 200), 'radius': 50 }]) ] substrate = [Layer(width=si('50um'), material=GaAs)] spacer = [Layer(width=si('25nm'), material=SiO2)] solar_cell = SolarCell(spacer + GaAs_junction + substrate) # solar cell with SiO2 coating opts.optics_method = 'TMM' solar_cell_solver(solar_cell, 'qe', opts) TMM_EQE = solar_cell[1].eqe(opts.wavelength) opts.optics_method = 'BL' solar_cell_solver(solar_cell, 'qe', opts) BL_EQE = solar_cell[1].eqe(opts.wavelength) solar_cell = SolarCell(spacer + GaAs_junction + DBR + substrate) # as above, with a DBR on the back opts.optics_method = 'TMM' solar_cell_solver(solar_cell, 'qe', opts) TMM_EQE_DBR = solar_cell[1].eqe(opts.wavelength) solar_cell = SolarCell(NP_layer + spacer + GaAs_junction + DBR + substrate) # cell with TiO2 nanocylinder array on the front
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.relative_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'), Layer(si("100um"), material=bot_cell_p_material, role='base'), ], sn=1, sp=1, kind='DA'), ], reflectivity=ref, shading=0.08, cell_area=0.7 * 0.7 / 1e4) wl = np.linspace(300, 1800, 700) * 1e-9 solar_cell_solver(solar_cell, 'qe', user_options={'wavelength': wl}) plt.figure(1) plt.plot(wl * 1e9, solar_cell[0].eqe(wl) * 100, 'b', label='GaInP') plt.plot(wl * 1e9, solar_cell[1].eqe(wl) * 100, 'g', label='InGaAs') plt.plot(wl * 1e9, solar_cell[2].eqe(wl) * 100, 'r', label='Ge')
output = Junction([ Layer(width=si('30nm'), material=window, role="Window"), Layer(width=si('150nm'), material=p_AlGaAs, role="Emitter"), Layer(width=si('1000nm'), material=n_AlGaAs, role="Base"), Layer(width=si('200nm'), material=bsf, role="BSF") ], sn=1e6, sp=1e6, T=T, kind='PDD') return output my_solar_cell = SolarCell([AlGaAs(T), default_GaAs(T)], T=T, R_series=0, substrate=substrate) 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) # We calculate the IV curve under illumination solar_cell_solver(my_solar_cell, 'iv', user_options={
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
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], linestyle='-',
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.relative_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'), # Uncomment the following to add the Ge junction. The calculation will be MUCH longer. # Junction([Layer(si("400nm"), material=bot_cell_n_material, role='emitter'), # Layer(si("100um"), material=bot_cell_p_material, role='base'), # ], sn=1, sp=1, kind='DA'), ], reflectivity=ref, shading=0.08, cell_area=0.7 * 0.7 / 1e4) wl = np.linspace(300, 1800, 700) * 1e-9 solar_cell_solver(solar_cell, 'qe', user_options={'wavelength': wl}) plt.figure(1) plt.plot(wl * 1e9, solar_cell[0].eqe(wl) * 100, 'b', label='GaInP') plt.plot(wl * 1e9, solar_cell[1].eqe(wl) * 100, 'g', label='InGaAs') # Uncomment to plot the Ge junction # plt.plot(wl * 1e9, solar_cell[2].eqe(wl) * 100, 'r', label='Ge')
def __init__(self): GaAs = material('GaAs_WVASE')() Air = material('Air')() InAlP = material('InAlP_WVASE')() InGaP = material('InGaP_WVASE')() Ag = material('Ag_Jiang')() SiN = material('SiN_SE')() x = 600 # anti-reflection coating ARC1 = [Layer(si('60nm'), SiN)] size = ((x, 0), (x / 2, np.sin(np.pi / 3) * x)) wavelengths = np.linspace(250, 930, 200) * 1e-9 RCWA_wl = wavelengths options = { 'nm_spacing': 0.5, 'n_theta_bins': 100, 'c_azimuth': 1e-7, 'pol': 's', 'wavelengths': RCWA_wl, 'theta_in': 0, 'phi_in': 0, 'parallel': True, 'n_jobs': -1, 'phi_symmetry': np.pi / 2, 'project_name': 'ultrathin' } ropt = dict(LatticeTruncation='Circular', DiscretizedEpsilon=True, DiscretizationResolution=4, PolarizationDecomposition=False, PolarizationBasis='Default', LanczosSmoothing=True, SubpixelSmoothing=True, ConserveMemory=False, WeismannFormulation=True) options['rcwa_options'] = ropt grating = [ Layer(si(100, 'nm'), SiN, geometry=[{ 'type': 'circle', 'mat': Ag, 'center': (0, 0), 'radius': 100, 'angle': 0 }]) ] solar_cell = SolarCell(ARC1 + [ Layer(material=InGaP, width=si('19nm')), Layer(material=GaAs, width=si('86nm')), Layer(material=InAlP, width=si('18nm')) ] + grating) self.S4_setup = rcwa_structure(solar_cell, size, 37, options, Air, Ag) spect = np.loadtxt('AM0.csv', delimiter=',') self.AM0 = interp1d(spect[:, 0], spect[:, 1])(wavelengths * 1e9)
def plot(self, x, icol=0): if icol == 'with_ARC' or icol == 'ideal_with_ARC' or icol == 'ideal' or icol == 'best': cols = sns.color_palette("husl", 4) else: cols = sns.cubehelix_palette(len(use_orders), start=.5, rot=-.9, reverse=True) GaAs = material('GaAs_WVASE')() Air = material('Air')() InAlP = material('InAlP_WVASE')() InGaP = material('InGaP_WVASE')() Ag = material('Ag_Jiang')() # MgF2 = material('MgF2')() # Ta2O5 = material('410', nk_db=True)() SiN = material('SiN_SE')() grating1 = [Layer(si(x[2] * x[1], 'nm'), SiN)] grating2 = [ Layer(si((1 - x[1]) * x[2], 'nm'), SiN, geometry=[{ 'type': 'circle', 'mat': Ag, 'center': (0, 0), 'radius': x[3], 'angle': 0 }]) ] if icol == 'with_ARC' or icol == 'ideal_with_ARC': Al2O3 = material('354', nk_db=True)() solar_cell = SolarCell([ Layer(material=Al2O3, width=si('70nm')), Layer(material=InGaP, width=si('19nm')), Layer(material=GaAs, width=si(x[0], 'nm')), Layer(material=InAlP, width=si('18nm')) ] + grating1 + grating2, substrate=Ag) S4_setup = rcwa_structure(solar_cell, self.size, self.orders, self.options, Air, Ag) RAT = S4_setup.calculate() EQE_sim = 0.9 * 100 * (RAT['A_layer'][:, 1] + RAT['A_layer'][:, 2]) Acomb = RAT['A_layer'][:, 2] + RAT['A_layer'][:, 1] Jsc = np.round( 0.1 * (q / (h * c)) * np.trapz(S4_setup.wavelengths * 1e9 * Acomb * self.AM0, S4_setup.wavelengths * 1e9) / 1e9, 1) print('Jsc with ARC ', Jsc) else: solar_cell = SolarCell([ Layer(material=InGaP, width=si('19nm')), Layer(material=GaAs, width=si(x[0], 'nm')), Layer(material=InAlP, width=si('18nm')) ] + grating1 + grating2, substrate=Ag) S4_setup = rcwa_structure(solar_cell, self.size, self.orders, self.options, Air, Ag) RAT = S4_setup.calculate() EQE_sim = 0.9 * 100 * (RAT['A_layer'][:, 0] + RAT['A_layer'][:, 1]) Acomb = RAT['A_layer'][:, 0] + RAT['A_layer'][:, 1] Jsc = np.round( 0.1 * (q / (h * c)) * np.trapz(S4_setup.wavelengths * 1e9 * Acomb * self.AM0, S4_setup.wavelengths * 1e9) / 1e9, 1) print('Jsc without ARC ', Jsc) # plt.figure() if icol == 'ideal': plt.plot(S4_setup.wavelengths * 1e9, EQE_sim, color=cols[0], label='Fully etched (' + str(Jsc) + ')') elif icol == 'with_ARC': plt.plot(S4_setup.wavelengths * 1e9, EQE_sim, '--', color=cols[1], label='Partly etched ith ARC (' + str(Jsc) + ')') elif icol == 'ideal_with_ARC': plt.plot(S4_setup.wavelengths * 1e9, EQE_sim, '--', color=cols[2], label='Fully etched with ARC (' + str(Jsc) + ')') elif icol == 'best': plt.plot(S4_setup.wavelengths * 1e9, EQE_sim, color=cols[3], label='Best fit (' + str(Jsc) + ')') else: plt.plot(S4_setup.wavelengths * 1e9, EQE_sim, label='n = ' + str(self.orders), color=cols[icol]) # plt.show() return S4_setup.wavelengths * 1e9, self.EQE_meas