def evaluate(self, x): light_source = LightSource(source_type='standard', version='AM1.5g') wl = np.linspace(300, 1850, 500) * 1e-9 solar_cell = self.make_cell(x) position = [1e-10] * 10 + [5e-8] V = np.linspace(0, 3.5, 300) solar_cell_solver(solar_cell, 'iv', user_options={ 'voltages': V, 'light_iv': True, 'wavelength': wl, 'mpp': True, 'light_source': light_source, 'optics_method': 'TMM', 'BL_correction': True, 'position': position }) efficiency = solar_cell.iv["Eta"] # print('Efficiency =', efficiency) return -efficiency
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 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 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_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 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.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])
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])
GaAs = material('GaAs')(T=T) thin_GaAs = SolarCell([Layer(material=GaAs, width=si('500nm'))]) GaAs_on_substrate = SolarCell([Layer(material=GaAs, width=si('500nm'))], substrate=GaAs) wl = si(np.linspace(300, 900, 200), 'nm') # Thin solar cell, no substrate - will get significant absorption enhancement from reflection at the GaAs/air interface at the back # MUST specify no_back_reflection = False, so that Solcore does not automatically suppress reflections from the back # (currently, the default setting in solcore is to suppress reflections from the back, so no_back_reflection = True solar_cell_solver(thin_GaAs, 'optics', user_options={ 'wavelength': wl, 'optics_method': 'TMM', 'no_back_reflection': False }) z_pos = np.linspace(0, thin_GaAs.width, 201) profiles_thin = thin_GaAs[0].absorbed(z_pos) # Same thin solar cell, but now on a GaAs substrate. In this case, we get the same result whether or not we specify # no_back_reflection to be True or False, since with a GaAs on GaAs cell we don't get any reflection at the back interface anyway solar_cell_solver(GaAs_on_substrate, 'optics', user_options={ 'wavelength': wl, 'optics_method': 'TMM' }) profiles_thick = GaAs_on_substrate[0].absorbed(z_pos)
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') plt.legend() plt.ylim(0, 100) plt.ylabel('EQE (%)') plt.xlabel('Wavelength (nm)') V = np.linspace(0, 3, 300) solar_cell_solver(solar_cell, 'iv', user_options={'voltages': V, 'light_iv': True, 'wavelength': wl}) plt.figure(2)
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={ '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 }) # We can plot the electron and hole densities in equilibrium and at short circuit, both calculated automatically # before calculating the IV curve plt.figure(1) for j in my_solar_cell.junction_indices: zz = my_solar_cell[j].short_circuit_data.Bandstructure[ 'x'] + my_solar_cell[j].offset n = my_solar_cell[j].short_circuit_data.Bandstructure['n'] p = my_solar_cell[j].short_circuit_data.Bandstructure['p']
["c", "c", "c", "i"], ["c", "i", "i", "c"], ["i", "i", "i", "i"], ] titles = [ "All coherent", "Bottom Ge layer explicity incoherent", "Both layers of GaAs junction incoherent", "All layers incoherent", ] for i1, cl in enumerate(c_list): plt.figure(i1) options.coherency_list = cl solar_cell_solver(optical_struct, "optics", options) plt.plot(wl * 1e9, optical_struct[0].layer_absorption) plt.plot(wl * 1e9, optical_struct[1].layer_absorption) plt.plot(wl * 1e9, optical_struct[2].layer_absorption) plt.plot(wl * 1e9, optical_struct.reflected, "--") plt.plot(wl * 1e9, optical_struct.transmitted, "--") plt.plot( wl * 1e9, optical_struct[0].layer_absorption + optical_struct[1].layer_absorption + optical_struct[2].layer_absorption + optical_struct.reflected + optical_struct.transmitted, ) plt.legend(["GaInP", "GaAs", "Ge", "R", "T", "R+A+T"], loc="upper left") plt.title(titles[i1]) plt.xlabel("Wavelength (nm)")
def plot(self, x): light_source = LightSource(source_type='standard', version='AM1.5g') wl = np.linspace(300, 1850, 500) * 1e-9 solar_cell = self.make_cell(x) position = [1e-10] * 10 + [5e-8] V = np.linspace(0, 3.5, 300) solar_cell_solver(solar_cell, 'iv', user_options={ 'voltages': V, 'light_iv': True, 'wavelength': wl, 'mpp': True, 'light_source': light_source, 'optics_method': 'TMM', 'BL_correction': True, 'position': position }) efficiency = solar_cell.iv["Eta"] pmax = solar_cell.iv["Pmpp"] ff = solar_cell.iv["FF"] voc = solar_cell.iv["Voc"] isc = solar_cell.iv["Isc"] plt.figure() plt.plot(V, solar_cell.iv['IV'][1] / 10, 'k', linewidth=3, label='Total') plt.plot(V, -solar_cell[2].iv(V) / 10, 'b', label='GaInP') plt.plot(V, -solar_cell[3].iv(V) / 10, 'g', label='GaAs') plt.plot(V, -solar_cell[4].iv(V) / 10, 'r', label='SiGeSn') plt.plot(V, -solar_cell[5].iv(V) / 10, 'y', label='Ge') plt.text(2, 10, '$\eta = $' + str(round(efficiency * 100, 1)) + '%') plt.text(2, 8, 'Pmax=' + str(round(pmax, 1)) + 'W/m$^2$') plt.text(2, 9, 'FF = ' + str(round(ff * 100, 1)) + '%') plt.text(2, 7, 'Voc=' + str(round(voc, 1)) + 'V') plt.text(2, 6, 'Jsc=' + str(round(0.1 * isc, 1)) + 'mA/cm$^2$') plt.legend() plt.ylim(0, 18) plt.xlim(0, 3.5) plt.ylabel('Current (mA/cm$^2$)') plt.xlabel('Voltage (V)') plt.show() # print('Efficiency =', efficiency) solar_cell_solver(solar_cell, 'qe', user_options={ 'wavelength': wl, 'optics_method': 'TMM', 'BL_correction': True, 'position': position }) plt.figure() plt.plot(wl * 1e9, solar_cell[2].eqe(wl) * 100, 'b', label='InGaP') plt.plot(wl * 1e9, solar_cell[3].eqe(wl) * 100, 'g', label='InGaAs') plt.plot(wl * 1e9, solar_cell[4].eqe(wl) * 100, 'r', label='SiGeSn') plt.plot(wl * 1e9, solar_cell[5].eqe(wl) * 100, 'y', label='Ge') plt.plot(wl * 1e9, solar_cell.absorbed * 100, 'k--', label='Absorption') # plt.plot(wl * 1e9, solar_cell[5].eqe(wl)*100, 'y', label='Ge') plt.legend(loc='upper right') plt.xlim(290, 1850) plt.ylim(0, 100) plt.ylabel('EQE (%)') plt.xlabel('Wavelength (nm)') plt.show()
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='-', label=names[c])
light_source = LightSource( source_type="standard", version="AM1.5g", x=wl, output_units="photon_flux_per_m", concentration=1, ) # The definitions are all done, so we just start solving the properties, # starting with the QE. We calculate the QE curve under illumination solar_cell_solver( my_solar_cell, "qe", user_options={ "light_source": light_source, "wavelength": wl, "optics_method": "TMM", }, ) # And now, the IV curves under various concentration levels. # NOTE: Due to the presence of QWs and the fact we calculate things a 19 different # concentrations, this might take a while (~4 hours). # Remove the QWs as indicated above to test the code much faster. num_con = 19 con = np.logspace(0, 3, num_con) vint = np.linspace(-3.5, 4, 600) V = np.linspace(-3.5, 0, 300)
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, 'db_mode': 'boltzmann', 'voltages': V, 'light_iv': True, 'wavelength': wl, 'optics_method': 'BL', 'light_source': light_source, 'position': z, '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',
# 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() plt.plot( wl * 1e9, optical_struct[0].layer_absorption + optical_struct[1].layer_absorption) plt.plot(wl * 1e9, optical_struct[2].layer_absorption) plt.plot(wl * 1e9, optical_struct[3].layer_absorption) plt.plot(wl * 1e9, optical_struct[100].layer_absorption) plt.plot(wl * 1e9, optical_struct.absorbed, '--') plt.plot(wl * 1e9, optical_struct.transmitted, '--') plt.plot(wl * 1e9, optical_struct.reflected, '--') plt.legend(['ARC', 'top', 'middle', 'bottom', 'A', 'T', 'R']) plt.ylim(0, 1) plt.ylabel('Absorption/Transmission/Reflection') plt.xlabel('Wavelength (nm)')
T=T, substrate=n_GaAs) light_source = LightSource(source_type='standard', version='AM1.5g', x=wl, output_units='photon_flux_per_m', concentration=1) # The definitions are all done, so we just start solving the properties, starting with the QE. # We calculate the QE curve under illumination solar_cell_solver(my_solar_cell, 'qe', user_options={ 'light_source': light_source, 'wavelength': wl, 'optics_method': 'TMM' }) # And now, the IV curves under various concentration levels. # NOTE: Due to the presence of QWs and the fact we calculate things a 19 different concentrations, this might take a # while (~4 hours). Remove the QWs as indicated above to test the code much faster. num_con = 3 con = np.logspace(0, 3, num_con) vint = np.linspace(-3.5, 4, 600) V = np.linspace(-3.5, 0, 300) allI = [] isc = []
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 opts.optics_method = 'RCWA' opts.orders = 49 # number of diffraction orders to keep in the RCWA solver
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. for i in range(my_solar_cell.junctions): ax[k].plot(V, -my_solar_cell(i).iv(V), color[i], linewidth=2, label=label[i]) ax[k].set_ylim(0, 300)
], 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) reflected = solar_cell.reflected absorbed_in_Si = solar_cell[1].layer_absorption interp_ref = interp1d(options.wavelength, reflected) interp_totalA = interp1d(options.wavelength, solar_cell[1].layer_absorption) wavelengths_external = np.linspace(301, 1199, 800) * 1e-9 alpha = n_material.alpha(wavelengths_external) A_layer = interp_totalA(wavelengths_external) junction_width = n_material_width + p_material_width
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
def solve_quasi_3D(solar_cell, injection, contacts, options=None, Lx=10e-6, Ly=10e-6, h=2e-6, R_back=1e-16, R_contact=1e-16, R_line=1e-16, bias_start=0, bias_end=1.8, bias_step=0.01): """ Entry function for the quasi-3D solver :param solar_cell: A solar cell object :param injection: 2D array indicating the (optical) injection mask :param contacts: 2D array indicating the electrical contacts :param options: Options for the 1D solar cell solver :param Lx: Pixel size in the X direction :param Ly: Pixel size in the Y direction :param h: Height of the metal fingers :param R_back: Resistance back contact :param R_contact: Contact resistance :param R_line: Resistivity metal fingers :param bias_start: Initial voltage (V) :param bias_end: Final voltage (V) :param bias_step: Voltage step (V) :return: A tuple with: - V [steps + 1] : 1D Array with the external voltages - I [steps + 1] : 1D Array with the current at all external V - Vall [xnodes, ynodes, 2 * junctions, steps + 1] : 4D Array with the voltages in all nodes, at all external V - Vmet [xnodes, ynodes, steps + 1] : 3D Array with the voltages in the metal nodes, at all external V """ # We first start by the solar cell as if it were a normal, isolated cell print("Solving 1D Solar Cell...") solar_cell_solver(solar_cell, 'iv', user_options=options) print("... Done!\n") # We don't care about this IV curve, in principle, but we care about some of the parameters calculated, like jsc, # j01 or j02 if calculated from detailed balance. We extract those parameters from the cell totaljuncs = solar_cell.junctions Isc_array = np.zeros(totaljuncs) I01_array = np.zeros(totaljuncs) n1_array = np.zeros(totaljuncs) I02_array = np.zeros(totaljuncs) n2_array = np.zeros(totaljuncs) Eg_array = np.zeros(totaljuncs) rsh_array = np.zeros(totaljuncs) rshTop_array = np.zeros(totaljuncs) rshBot_array = np.zeros(totaljuncs) rseries_array = np.ones(totaljuncs) * 1e-16 for i in range(totaljuncs): n1_array[i] = solar_cell(i).n1 if hasattr(solar_cell(i), 'n1') else 1 n2_array[i] = solar_cell(i).n2 if hasattr(solar_cell(i), 'n2') else 2 rsh_array[i] = min(solar_cell(i).R_shunt, 1e16) if hasattr( solar_cell(i), 'R_shunt') else 1e16 rshTop_array[i] = max(solar_cell(i).R_sheet_top, 1e-16) if hasattr( solar_cell(i), 'R_sheet_top') else 1e-16 rshBot_array[i] = max(solar_cell(i).R_sheet_bot, 1e-16) if hasattr( solar_cell(i), 'R_sheet_bot') else 1e-16 try: Isc_array[i] = solar_cell(i).jsc I01_array[i] = solar_cell(i).j01 I02_array[i] = solar_cell(i).j02 Eg_array[i] = solar_cell(i).Eg except AttributeError as err: raise AttributeError( 'ERROR in quasi-3D solver: Junction is missing one essential argument. {}' .format(err)) j = 0 for i in solar_cell.tunnel_indices: rseries_array[j] = max(solar_cell[i].R_series, 1e-16) if hasattr( solar_cell[i], 'R_series') else 1e-16 j += 1 rseries_array[-1] = max(R_back, 1e-16) print("Solving quasi-3D Solar Cell...") V, I, Vall, Vmet = solve_circuit_quasi3D( bias_start, bias_end, bias_step, Isc_array, I01_array, I02_array, n1_array, n2_array, Eg_array, rsh_array, rseries_array, injection, contacts, rshTop_array, rshBot_array, R_line / h, R_contact, Lx, Ly) print("... Done!!") return V, I, Vall, Vmet
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) if __name__ == '__main__': # We calculate the IV curve under illumination solar_cell_solver(my_solar_cell, 'equilibrium') # , # 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}) # We can plot the electron and hole densities in equilibrium and at short circuit, both calculated automatically # before calculating the IV curve plt.figure(1) for j in my_solar_cell.junction_indices: zz = my_solar_cell[j].equilibrium_data.Properties['x'] + my_solar_cell[ j].offset n = my_solar_cell[j].equilibrium_data.Properties['Nd'] p = my_solar_cell[j].equilibrium_data.Properties['Na'] plt.semilogy(zz, n, 'b') plt.semilogy(zz, p, 'r')