├── .gitattributes ├── 1D ├── Multi-Layer_Devices │ ├── Organic_bilayer_solar_cell │ │ ├── Gummel_method │ │ │ ├── BernoulliFnc_n.m │ │ │ ├── BernoulliFnc_p.m │ │ │ ├── CalTrap.m │ │ │ ├── DD_Bilayer_OSC_Gummel_method.m │ │ │ ├── R.m │ │ │ ├── SetAn_val.m │ │ │ ├── SetAp_val.m │ │ │ ├── Setbn.m │ │ │ ├── Setbp.m │ │ │ ├── f_dR_SRH_A.m │ │ │ ├── f_dR_SRH_D.m │ │ │ ├── f_of_zeta.m │ │ │ ├── get_data.m │ │ │ └── kppd.m │ │ └── Newton-Raphson_method │ │ │ ├── BernoulliFnc_n.m │ │ │ ├── BernoulliFnc_p.m │ │ │ ├── CalTrap.m │ │ │ ├── Continuity_n_fnc.m │ │ │ ├── Continuity_p_fnc.m │ │ │ ├── DD_Bilayer_OSC_Newton_method.m │ │ │ ├── FindJacobian.m │ │ │ ├── Fu.m │ │ │ ├── Net_Genfnc.m │ │ │ ├── Poissonfnc.m │ │ │ ├── R.m │ │ │ ├── RecalculateU.m │ │ │ ├── SetAn_val.m │ │ │ ├── SetAp_val.m │ │ │ ├── Setbn.m │ │ │ ├── Setbp.m │ │ │ ├── dBni_dVi.m │ │ │ ├── dBni_dVi_min1.m │ │ │ ├── dBpi_dVi.m │ │ │ ├── dBpi_dVi_min1.m │ │ │ ├── dFn_dV.m │ │ │ ├── dFn_dn.m │ │ │ ├── dFp_dV.m │ │ │ ├── dFp_dp.m │ │ │ ├── dn_dx.m │ │ │ ├── dnegBni_dVi.m │ │ │ ├── dnegBni_dVi_min1.m │ │ │ ├── dnegBpi_dVi.m │ │ │ ├── dnegBpi_dVi_min1.m │ │ │ ├── dp_dx.m │ │ │ ├── f_dR_SRH_A.m │ │ │ ├── f_dR_SRH_D.m │ │ │ ├── f_of_zeta.m │ │ │ ├── kppd.m │ │ │ ├── nTraps_fnc.m │ │ │ └── pTraps_fnc.m │ └── Perovskite_solar_cell │ │ ├── BernoulliFnc_n.m │ │ ├── BernoulliFnc_p.m │ │ ├── GenerationRate.m │ │ ├── Perovskites_Main.m │ │ ├── README.md │ │ ├── R_Langevin.m │ │ ├── R_SRH_ETL.m │ │ ├── R_SRH_HTL.m │ │ ├── SetAV_val.m │ │ ├── SetAn_val.m │ │ ├── SetAp_val.m │ │ ├── Setbn.m │ │ ├── Setbp.m │ │ ├── gen_rate.txt │ │ └── plot_JVs.m ├── README.md └── Single_Layer_Devices │ ├── Drift-Diffusion │ ├── Single-charge-carrier │ │ ├── Benchmarks │ │ │ ├── Scharfetter-Gummel │ │ │ │ ├── U_10_32_new │ │ │ │ │ ├── 1p1V_Jp.jpg │ │ │ │ │ ├── 1p1V_V.jpg │ │ │ │ │ ├── 1pt1V_E.jpg │ │ │ │ │ └── 1pt1V_p.jpg │ │ │ │ ├── U_negative10e30_new │ │ │ │ │ ├── 1p11_V_Jp.jpg │ │ │ │ │ ├── 1p1V_p.jpg │ │ │ │ │ ├── 1pt1V_E.jpg │ │ │ │ │ └── 1pt1V_V.jpg │ │ │ │ └── no_generation │ │ │ │ │ ├── E_1V.jpg │ │ │ │ │ ├── V_1V.jpg │ │ │ │ │ ├── convergence 1V.jpg │ │ │ │ │ └── p_1V.jpg │ │ │ └── Slotboom │ │ │ │ ├── U_10e30 │ │ │ │ ├── E_1V.jpg │ │ │ │ ├── V_1V.jpg │ │ │ │ └── p_1V.jpg │ │ │ │ ├── U_10e32 │ │ │ │ ├── E_10V.jpg │ │ │ │ ├── E_1V.jpg │ │ │ │ ├── E_20.jpg │ │ │ │ ├── V_10V.jpg │ │ │ │ ├── V_1V.jpg │ │ │ │ ├── V_20V.jpg │ │ │ │ ├── convergence_1V.jpg │ │ │ │ ├── p_10V.jpg │ │ │ │ ├── p_1V.jpg │ │ │ │ └── p_20V.jpg │ │ │ │ ├── U_negative10e30 │ │ │ │ ├── E_1V.jpg │ │ │ │ ├── V_1V.jpg │ │ │ │ └── p_1V.jpg │ │ │ │ └── no_generation │ │ │ │ ├── E_1V.jpg │ │ │ │ ├── V_1V.jpg │ │ │ │ ├── convergence_1V.jpg │ │ │ │ └── p_1V.jpg │ │ └── src │ │ │ ├── FD_drift_diffusion_EachfromIC_w_generation.m │ │ │ ├── Scharfetter-Gummel │ │ │ ├── 200.00V.txt │ │ │ ├── BernoulliFnc.m │ │ │ ├── SG_1carrier_Mott_Gurney_attempt_20200705.m │ │ │ ├── SG_1carrier_high_field.m │ │ │ ├── SG_1carrier_high_field_BETTER_plotting.m │ │ │ ├── SG_1carrier_high_field_test.m │ │ │ ├── SG_1carrier_low_field.m │ │ │ ├── SetAp_val.m │ │ │ └── Setbp.m │ │ │ └── Slotboom_variables │ │ │ ├── Slotboom_DD_high_field.m │ │ │ ├── Slotboom_DD_high_field_reindex.m │ │ │ ├── Slotboom_DD_high_field_reindex_test_Jp.m │ │ │ └── Slotboom_DD_low_field.m │ └── Two-charge-carriers │ │ ├── C++ implementation │ │ ├── .gitignore │ │ ├── 1D_general_DD.pro │ │ ├── README.md │ │ ├── Utilities.cpp │ │ ├── Utilities.h │ │ ├── constants.h │ │ ├── continuity_n.cpp │ │ ├── continuity_n.h │ │ ├── continuity_p.cpp │ │ ├── continuity_p.h │ │ ├── experiment_JV.inp │ │ ├── gen_rate.inp │ │ ├── main.cpp │ │ ├── make │ │ ├── optimization.cpp │ │ ├── optimization.h │ │ ├── parameters.cpp │ │ ├── parameters.h │ │ ├── parameters.inp │ │ ├── photogeneration.cpp │ │ ├── photogeneration.h │ │ ├── poisson.cpp │ │ ├── poisson.h │ │ ├── recombination.cpp │ │ ├── recombination.h │ │ ├── run_DD.cpp │ │ ├── run_dd.h │ │ ├── thomas_tridiag_solve.cpp │ │ ├── thomas_tridiag_solve.h │ │ ├── v1.0 │ │ │ ├── .gitignore │ │ │ ├── 1D_general_DD.pro │ │ │ ├── README.md │ │ │ ├── Set_diagonals.cpp │ │ │ ├── Set_diagonals.h │ │ │ ├── bernoulli.cpp │ │ │ ├── bernoulli.h │ │ │ ├── main.cpp │ │ │ ├── make │ │ │ ├── parameters.h │ │ │ ├── photogeneration.cpp │ │ │ ├── photogeneration.h │ │ │ ├── recombination.cpp │ │ │ ├── recombination.h │ │ │ ├── set_rhs.cpp │ │ │ ├── set_rhs.h │ │ │ ├── thomas_tridiag_solve.cpp │ │ │ └── thomas_tridiag_solve.h │ │ └── v2.0 │ │ │ ├── 1D_general_DD_v2.pro │ │ │ ├── Utilities.cpp │ │ │ ├── Utilities.h │ │ │ ├── constants.h │ │ │ ├── continuity_n.cpp │ │ │ ├── continuity_n.h │ │ │ ├── continuity_p.cpp │ │ │ ├── continuity_p.h │ │ │ ├── gen_rate.inp │ │ │ ├── main.cpp │ │ │ ├── make │ │ │ ├── parameters.cpp │ │ │ ├── parameters.h │ │ │ ├── parameters.inp │ │ │ ├── photogeneration.cpp │ │ │ ├── photogeneration.h │ │ │ ├── poisson.cpp │ │ │ ├── poisson.h │ │ │ ├── recombination.cpp │ │ │ ├── recombination.h │ │ │ ├── thomas_tridiag_solve.cpp │ │ │ └── thomas_tridiag_solve.h │ │ ├── Matlab implementation │ │ ├── .gitignore │ │ ├── BernoulliFnc_n.m │ │ ├── BernoulliFnc_p.m │ │ ├── DD_2carriers_Main.m │ │ ├── GenerationRate.m │ │ ├── README.md │ │ ├── R_Langevin.m │ │ ├── SetAV_val.m │ │ ├── SetAn_val.m │ │ ├── SetAp_val.m │ │ ├── Setbn.m │ │ ├── Setbp.m │ │ ├── gen_rate.inp │ │ ├── plot_JVs.m │ │ └── plot_carrier_densities.m │ │ ├── build-1D_general_DD-Desktop_Qt_5_8_0_MSVC2015_64bit-Debug │ │ ├── .qmake.stash │ │ ├── Makefile │ │ ├── Makefile.Debug │ │ └── Makefile.Release │ │ └── build-1D_general_DD-Desktop_Qt_5_8_0_MSVC2015_64bit-Release │ │ ├── .qmake.stash │ │ ├── Makefile │ │ ├── Makefile.Debug │ │ └── Makefile.Release │ ├── Mott-Gurney_law │ ├── Benchmarks │ │ ├── 0pt05nmMesh_tridiag │ │ │ ├── E_200V__tridiag_0pt05nmMesh.jpg │ │ │ ├── E_200V_convergence_0pt05nmMesh.jpg │ │ │ ├── JV_0pt05nmMesh_tridiag_result.jpg │ │ │ ├── J_200V_tridiag_0pt05nmMesh.jpg │ │ │ └── p_200V_tridiag_0pt05nmMesh.jpg │ │ └── 1nmMesh_tridiag │ │ │ ├── 17.00V.txt │ │ │ ├── 18.00V.txt │ │ │ ├── 19.00V.txt │ │ │ ├── 20.00V.txt │ │ │ ├── 21.00V.txt │ │ │ ├── 22.00V.txt │ │ │ ├── 23.00V.txt │ │ │ ├── 24.00V.txt │ │ │ ├── 25.00V.txt │ │ │ ├── 26.00V.txt │ │ │ ├── 27.00V.txt │ │ │ ├── 28.00V.txt │ │ │ ├── 29.00V.txt │ │ │ ├── 30.00V.txt │ │ │ ├── E.jpg │ │ │ ├── E_200V.jpg │ │ │ ├── J.jpg │ │ │ ├── JV.jpg │ │ │ ├── JV_10_to_20.jpg │ │ │ ├── JV_from200V.jpg │ │ │ ├── J_200V.jpg │ │ │ ├── convergence_last_Va.jpg │ │ │ ├── p.jpg │ │ │ └── p_200V.jpg │ └── src │ │ ├── WENO5_1d_reconstruction.m │ │ ├── WENO5_Mott_Gurney_Va_decreasing.m │ │ ├── WENO5_Mott_Gurney_appliedE_Euler.m │ │ ├── WENO5_Mott_Gurney_appliedE_vVerlet.m │ │ ├── WENO5_Mott_Gurney_appliedE_vVerlet_testEa0BC.m │ │ ├── fluxSplit.m │ │ ├── license.txt │ │ ├── plotter.m │ │ └── residual.m │ └── README.md ├── 2D ├── .gitignore ├── README.md ├── Single-charge-carrier │ └── Matlab_implementation │ │ ├── Calculate_Bernoullis.m │ │ ├── DD_2D_holes_main.m │ │ ├── SetAV_2D.m │ │ ├── SetAp_2D.m │ │ ├── SetbV_2D.m │ │ └── Setbp_2D.m └── Two-charge-carriers │ ├── C++_implementation │ ├── 2D_DD.pro │ ├── README.md │ ├── Utilities.cpp │ ├── Utilities.h │ ├── constants.h │ ├── continuity_n.cpp │ ├── continuity_n.h │ ├── continuity_p.cpp │ ├── continuity_p.h │ ├── gen_rate.inp │ ├── gen_rate_large_device.inp │ ├── main.cpp │ ├── parameters.cpp │ ├── parameters.h │ ├── parameters.inp │ ├── parameters_large_device.inp │ ├── photogeneration.cpp │ ├── photogeneration.h │ ├── poisson.cpp │ ├── poisson.h │ ├── recombination.cpp │ ├── recombination.h │ └── short_vector_of_vectors_test.cpp │ └── Matlab_implementation │ ├── Calculate_Bernoullis_n.m │ ├── Calculate_Bernoullis_p.m │ ├── DD_2D_main.m │ ├── GenerationRate.m │ ├── SetAV_2D.m │ ├── SetAn_2D.m │ ├── SetAp_2D.m │ ├── SetbV_2D.m │ ├── Setbn_2D.m │ └── Setbp_2D.m ├── 3D ├── .gitignore ├── C++_implementation │ ├── Single-charge-carrier │ │ ├── 3D_DD_Cpp.pro │ │ ├── Mingw_version │ │ │ ├── Mingw_version.pro │ │ │ └── parameters.inp │ │ ├── README.md │ │ ├── Utilities.cpp │ │ ├── Utilities.h │ │ ├── build-Mingw_version-Desktop_Qt_5_11_1_MinGW_32bit-Debug │ │ │ └── parameters.inp │ │ ├── build-Mingw_version-Desktop_Qt_5_11_1_MinGW_32bit-Release │ │ │ └── parameters.inp │ │ ├── constants.h │ │ ├── continuity_p.cpp │ │ ├── continuity_p.h │ │ ├── debug │ │ │ └── untitled │ │ │ │ ├── main.cpp │ │ │ │ └── untitled.pro │ │ ├── main.cpp │ │ ├── parameters.cpp │ │ ├── parameters.h │ │ ├── parameters.inp │ │ ├── poisson.cpp │ │ └── poisson.h │ └── Two-charge-carriers │ │ ├── 3D_DD_Cpp.pro │ │ ├── README.md │ │ ├── Utilities.cpp │ │ ├── Utilities.h │ │ ├── constants.h │ │ ├── continuity_n.cpp │ │ ├── continuity_n.h │ │ ├── continuity_p.cpp │ │ ├── continuity_p.h │ │ ├── gen_rate.inp │ │ ├── main.cpp │ │ ├── parameters.cpp │ │ ├── parameters.h │ │ ├── parameters.inp │ │ ├── photogeneration.cpp │ │ ├── photogeneration.h │ │ ├── poisson.cpp │ │ ├── poisson.h │ │ ├── recombination.cpp │ │ └── recombination.h ├── Matlab_implementation │ ├── Single-charge-carrier │ │ ├── .gitignore │ │ ├── Calculate_Bernoullis_p.m │ │ ├── DD_3D_main_single_carrier.m │ │ ├── SetAV_3D.m │ │ ├── SetAp_3D.m │ │ ├── SetbV_3D.m │ │ └── Setbp_3D.m │ └── Two-charge-carriers │ │ ├── insulating_BCs_X_Y │ │ ├── Calculate_Bernoullis_n.m │ │ ├── Calculate_Bernoullis_p.m │ │ ├── DD_3D_main.m │ │ ├── DD_3D_main_no_gen_rate.m │ │ ├── GenerationRate.m │ │ ├── SetAV_3D.m │ │ ├── SetAn_3D.m │ │ ├── SetAn_3D_OLD.m │ │ ├── SetAp_3D.m │ │ ├── SetbV_3D.m │ │ ├── Setbn_3D.m │ │ └── Setbp_3D.m │ │ └── periodic_BCs_X_Y │ │ ├── Calculate_Bernoullis_p.asv │ │ ├── Calculate_Bernoullis_p.m │ │ ├── DD_3D_main_single_carrier_PBCs.m │ │ ├── SetAV_3D.asv │ │ ├── SetAV_3D.m │ │ ├── SetAp_3D.asv │ │ ├── SetAp_3D.m │ │ ├── SetbV_3D.asv │ │ ├── SetbV_3D.m │ │ └── Setbp_3D.m └── README.md ├── LICENSE └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Gummel_method/BernoulliFnc_n.m: -------------------------------------------------------------------------------- 1 | function B_n = BernoulliFnc_n(fullV) 2 | global num_cell phi_c Vt l gap_elec 3 | 4 | dV = zeros(1,num_cell+1); 5 | B_n = zeros(2, num_cell+1); 6 | 7 | %Bernoulli fnc. 8 | 9 | for i = 2:num_cell+1 %so dV(100) = dV(101: is at x=L) - dV(100, at x= l-dx). 10 | dV(i) = fullV(i)-fullV(i-1); %these fullV's ARE ACTUALLY PSI PRIME; ALREADY = V/Vt, when solved poisson. 11 | end 12 | 13 | dV(num_cell+1) = dV(num_cell+1) + phi_c/Vt; %injection step at right bndry (electrons electrode) 14 | dV(l+1) = dV(l+1) + gap_elec/Vt; %energy step at interface 15 | 16 | %note: adding a dV(l) here doesn't help--> hole densities still become 17 | %negative 18 | 19 | 20 | for i = 2:num_cell+1 21 | B_n(1,i) = dV(i)/(exp(dV(i))-1.0); %B(+dV) 22 | %B(2,i) = -dV(i)/(exp(-dV(i))-1.0); %THIS IS EQUIVALENT TO OTHE 23 | %BELOW EXPERSSION 24 | B_n(2,i) = B_n(1,i)*exp(dV(i)); %B(-dV) 25 | end 26 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Gummel_method/BernoulliFnc_p.m: -------------------------------------------------------------------------------- 1 | function B_p = BernoulliFnc_p(fullV) 2 | global num_cell phi_a Vt l gap_hole 3 | 4 | dV = zeros(1,num_cell+1); 5 | B_p = zeros(2, num_cell+1); 6 | 7 | %Bernoulli fnc. 8 | 9 | for i = 2:num_cell+1 %so dV(100) = dV(101: is at x=L) - dV(100, at x= l-dx). 10 | dV(i) = fullV(i)-fullV(i-1); %these fullV's ARE ACTUALLY PSI PRIME; ALREADY = V/Vt, when solved poisson. 11 | end 12 | 13 | dV(2) = dV(2) + phi_a/Vt; %injection step at left bndry (holes electrode) 14 | %dV(l+1) = dV(l+1) + gap_hole/Vt; %energy step at interface 15 | 16 | dV(l+1) = dV(l+1) + gap_hole/Vt; 17 | 18 | for i = 2:num_cell+1 19 | B_p(1,i) = dV(i)/(exp(dV(i))-1.0); %B(+dV) 20 | %B(2,i) = -dV(i)/(exp(-dV(i))-1.0); %THIS IS EQUIVALENT TO OTHE 21 | %BELOW EXPERSSION 22 | B_p(2,i) = B_p(1,i)*exp(dV(i)); %B(-dV) 23 | end 24 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Gummel_method/CalTrap.m: -------------------------------------------------------------------------------- 1 | function [pT_full, nT_full] = CalTrap(p_full, n_full) %allows to return 2 values with this notation 2 | global H_D H_A N N_HOMO N_LUMO T T_tD T_tA; 3 | 4 | pT_full = H_D*(p_full*N/N_HOMO).^(T/T_tD); %USE .^ for element wise (scalar) power 5 | nT_full = H_A*(n_full*N/N_LUMO).^(T/T_tA); 6 | 7 | pT_full = pT_full/N; 8 | nT_full = nT_full/N; 9 | 10 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Gummel_method/R.m: -------------------------------------------------------------------------------- 1 | function R = R(n_full, p_full) 2 | global gap_A gap_D q kb T N N_LUMO N_HOMO l; 3 | 4 | %Calcurate R_SRH with acceptor trap 5 | a = -gap_A*q; 6 | b = kb*T*log(n_full(l+1)*N/N_LUMO); %the log here is an ln 7 | n = 100; %I verified that 100 is enough to give reasonable result for the integration 8 | sumk = 0.0; 9 | 10 | for k= 1:n-1 11 | sumk = sumk + f_dR_SRH_A(a + k*(b-a)/n, n_full, p_full); %the first term here is E in f_dR_SRH_A 12 | end 13 | R_SRH_A = (b-a)/n*(f_dR_SRH_A(a, n_full, p_full)/2.0D0 + sumk + f_dR_SRH_A(b, n_full, p_full)/2.0D0); 14 | 15 | %Calcurate R_SRH with donor trap 16 | a = kb*T*log(p_full(l)*N/N_HOMO); 17 | b = -gap_D*q; 18 | n = 100; 19 | sumk = 0.0D0; 20 | 21 | for k=1:n-1 22 | sumk = sumk + f_dR_SRH_D(a + k*(b-a)/n, n_full, p_full); 23 | end 24 | R_SRH_D = (b-a)/n*(f_dR_SRH_D(a, n_full, p_full)/2.0D0 + sumk + f_dR_SRH_D(b, n_full, p_full)/2.0D0); 25 | 26 | R_SRH = R_SRH_A + R_SRH_D; 27 | R = R_SRH; -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Gummel_method/SetAn_val.m: -------------------------------------------------------------------------------- 1 | function An_val = SetAn_val(B_n) 2 | global num_elements n_mob 3 | 4 | An = zeros(num_elements,3); 5 | 6 | %BE CAREFUL!: the setup of the sparse matrix elements is 7 | %non-trivial: last entry of Ap(:,1) 1st entry of 8 | %Ap(:,3) are unused b/c off diagonals have 1 less element than main 9 | %diagonal! 10 | 11 | for i=1:num_elements %I verified that ordering of columns is correct! 12 | An(i,2) = -(n_mob*B_n(1,i+1) + n_mob*B_n(2,i+1+1)); %I HAVE VERIFIED THAT ALL THE dV's properly match up with indices! All the extra +1's are b/c B starts at i=2 (1st pt inside device). 13 | end 14 | for i = 1:num_elements-1 15 | An(i,1) = n_mob*B_n(2,i+1+1); %should be i+1+1 b/c have B(dVi)*p_i-1 (see fortran reindexed version) 16 | end 17 | An(num_elements, 1) = 0; %last element is unused 18 | for i = 2:num_elements 19 | An(i,3) = n_mob*B_n(1,i+1); %1st element here corresponds to the 1st row of matrix: so need to use B3 --> corresponding to p(3) 20 | end 21 | An(1,3) = 0; %1st element of Ap(:,3) is unused 22 | 23 | 24 | An_val = spdiags(An,-1:1,num_elements,num_elements); -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Gummel_method/SetAp_val.m: -------------------------------------------------------------------------------- 1 | function Ap_val = SetAp_val(B_p) 2 | global num_elements p_mob 3 | 4 | Ap = zeros(num_elements,3); 5 | 6 | %BE CAREFUL!: the setup of the sparse matrix elements is 7 | %non-trivial: last entry of Ap(:,1) 1st entry of 8 | %Ap(:,3) are unused b/c off diagonals have 1 less element than main 9 | %diagonal! 10 | 11 | for i=1:num_elements %I verified that ordering of columns is correct! 12 | Ap(i,2) = -(p_mob*B_p(2,i+1) + p_mob*B_p(1,i+1+1)); %I HAVE VERIFIED THAT ALL THE dV's properly match up with indices! All the extra +1's are b/c B starts at i=2 (1st pt inside device). 13 | end 14 | for i = 1:num_elements-1 15 | Ap(i,1) = p_mob*B_p(1,i+1+1); %should be i+1+1 b/c have B(dVi)*p_i-1 (see fortran reindexed version) 16 | end 17 | Ap(num_elements, 1) = 0; %last element is unused 18 | for i = 2:num_elements 19 | Ap(i,3) = p_mob*B_p(2,i+1); %1st element here corresponds to the 1st row of matrix: so need to use B3 --> corresponding to p(3) 20 | end 21 | Ap(1,3) = 0; %1st element of Ap(:,3) is unused 22 | 23 | 24 | Ap_val = spdiags(Ap,-1:1,num_elements,num_elements); -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Gummel_method/Setbn.m: -------------------------------------------------------------------------------- 1 | function bn = Setbn(B_n, n_full, Un) 2 | global l Cn num_cell num_elements n_mob; 3 | 4 | bn = zeros(num_elements,1); 5 | 6 | %enforce boundary conditions through bp 7 | bn(1) = -n_mob*B_n(2,2)*n_full(1); 8 | bn(num_elements) = -n_mob*B_n(1,num_cell+1)*n_full(num_cell+1); %THERE WAS A MISTAKE HERE!! HAD num_elements +1 instead of num_cell+1! so weren't using the electron injection barrier! b/c num_elements +1 = num_cell 9 | 10 | %introduce a net generation rate at 1 mesh point to the right of where 11 | %introduce the bp generation rate 12 | bn(l) = -Cn*Un; 13 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Gummel_method/Setbp.m: -------------------------------------------------------------------------------- 1 | function bp = Setbp(B_p, p_full, Up) 2 | global l Cp num_cell num_elements p_mob; 3 | 4 | bp = zeros(num_elements,1); 5 | 6 | %enforce boundary conditions through bp 7 | bp(1,1) = -p_mob*B_p(1,2)*p_full(1); 8 | bp(num_elements, 1) = -p_mob*B_p(2,num_cell+1)*p_full(num_cell+1); %ENFORCE RIGHT SIDE P IS = 0 9 | 10 | %introduce a net generation rate somewhere in the middle 11 | bp(l-1) = -Cp*Up; -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Gummel_method/f_dR_SRH_A.m: -------------------------------------------------------------------------------- 1 | function f_dR_SRH_A = f_dR_SRH_A(E, n_full, p_full) 2 | global gap_A q kb T T_tA cap_n cap_p N_LUMO N_HOMO Vt l N H_A 3 | 4 | n1A = N_LUMO*exp(E/(kb*T)); 5 | p1A = N_HOMO*exp((-gap_A*q-E)/(kb*T)); 6 | f_dR_SRH_A = H_A/(kb*T_tA)*exp(E/(kb*T_tA))*cap_n*cap_p*(n_full(l+1)*p_full(l)*N^2-N_LUMO*N_HOMO*exp(-gap_A/Vt))/(cap_n*(n_full(l+1)*N + n1A) + cap_p*(p_full(l)*N + p1A)); 7 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Gummel_method/f_dR_SRH_D.m: -------------------------------------------------------------------------------- 1 | function f_dR_SRH_D = f_dR_SRH_D(E, n_full, p_full) 2 | global gap_D q kb T T_tD cap_n cap_p N_LUMO N_HOMO Vt l N H_D 3 | 4 | 5 | n1D = N_LUMO*exp((-gap_D*q-E)/(kb*T)); 6 | p1D = N_HOMO*exp(E/(kb*T)); 7 | f_dR_SRH_D = -H_D/(kb*T_tD)*exp(E/(kb*T_tD))*cap_n*cap_p*(n_full(l+1)*p_full(l)*N^2 - N_LUMO*N_HOMO*exp(-gap_D/Vt))/(cap_n*(n_full(l+1)*N + n1D) + cap_p*(p_full(l)*N + p1D)); -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Gummel_method/f_of_zeta.m: -------------------------------------------------------------------------------- 1 | function fnc_of_zeta = f_of_zeta(zeta, F) %make returned value have diff name than fnc, to not have confusion 2 | global L_int k_rec Eb rc Vt 3 | fnc_of_zeta = 3.0D0./(4.0D0.*pi.*L_int^3).*k_rec.*exp((-Eb + F.*rc.*cos(zeta))./Vt); %the integrand -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Gummel_method/get_data.m: -------------------------------------------------------------------------------- 1 | 2 | [File,Path]=uigetfile('*.txt','MultiSelect','off'); 3 | 4 | str=sprintf('%s', [Path File]); %makes str be the name of file (along with its path) 5 | file=sprintf('%s',File); 6 | format shortG %change formating so doesn't show 0's for e-11 values. 7 | 8 | %Need to skip 1st row, b/c it has some other info in it so use import data 9 | %fnc. 10 | %data = importdata(str, ' ', 0); %importdata(FILENAME, DELIM, NHEADERLINES) loads data into a struct from 11 | %ASCII file FILENAME, reading numeric data starting from line NHEADERLINES+1. 12 | 13 | data = importdata(str) 14 | %struct has fields data and textdata 15 | position = data(:,1); 16 | fullV(2:73) = data(:,2); 17 | p_full = data(:,3)/N; %convert back to scaled form b/c need to calculate nT's! 18 | n_full = data(:,4)/N; 19 | 20 | 21 | pT_full = H_D*(p_full*N/N_HOMO).^(T/T_tD); %USE .^ for element wise (scalar) power 22 | nT_full = H_A*(n_full*N/N_LUMO).^(T/T_tA); 23 | 24 | pT_full = pT_full/N; 25 | nT_full = nT_full/N; 26 | 27 | 28 | %NOTE: Vbi is a constant for the given system--> depends on energy levels 29 | %of materials 30 | fullV(1) = 0.0; 31 | fullV(num_cell+1) = (Vbi -0.94)/Vt; %for intial guess: Va = 0, b/c is equil. run, so Vbi-Va --> Vbi 32 | %0.94, b/c want solution when 1st itertied to 0.94V 33 | 34 | 35 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Gummel_method/kppd.m: -------------------------------------------------------------------------------- 1 | function k_ppd = kppd(F) 2 | global q epsilon Vt Eb k_rec L_int 3 | 4 | 5 | if(F <= 0.0D0) %This performs the integral over half sphere for case when field opposes dissociation 6 | zeta1 = -pi/2.0D0; 7 | zeta2 = pi/2.0D0; 8 | n = 1000; 9 | sumk = 0.0D0; 10 | for k=1:n-1 11 | sumk = sumk + f_of_zeta(zeta1 + k*(zeta2-zeta1)/n, F); 12 | end 13 | k_ppd = (zeta2-zeta1)./n.*(f_of_zeta(zeta1, F)./2.0D0 + sumk + f_of_zeta(zeta1, F)./2.0D0); 14 | k_ppd = k_ppd./pi; 15 | else 16 | 17 | bk = q*F/(8*pi*epsilon*(Vt^2)); %b in Bessel fnc. experssion (**2 means ^2) %USE ./ to tell matlab that its element by element operation --> BUT THIS THEN RETURNS AN EMPTY MATRIX! 18 | k_ppd = (3.0.*k_rec./(4.0*pi.*L_int^3))*exp(-Eb./Vt)*(1.0 + bk + (bk^2)/3.0 + (bk^3)/18.0 + (bk^4)/180.0); %Bessel function is expanded to 4th order here 19 | end 20 | 21 | %not sure what these are for 22 | %bk = q*F/(8.0*pi*epsilon*Vt^2) 23 | %k_ppd2 = (3.0*k_rec/(4.0*pi*L_int^3))*exp(-Eb/Vt)*(1.0 + bk + (bk^2)/3.0 + (bk^3)/18.0D0 + (bk^4)/180.0D0); %Bessel function is expanded to 4th order here -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/BernoulliFnc_n.m: -------------------------------------------------------------------------------- 1 | function B_n = BernoulliFnc_n(fullV) 2 | global num_cell phi_c Vt l gap_elec 3 | 4 | dV = zeros(1,num_cell+1); 5 | B_n = zeros(2, num_cell+1); 6 | 7 | for i = 2:num_cell+1 % so dV(100) = dV(101: is at x=L) - dV(100, at x= l-dx). 8 | dV(i) = fullV(i)-fullV(i-1); % these fullV's = V/Vt, when solved poisson. 9 | end 10 | 11 | dV(num_cell+1) = dV(num_cell+1) + phi_c/Vt; %injection step at right bndry (electrons electrode) 12 | dV(l+1) = dV(l+1) + gap_elec/Vt; %energy step at interface 13 | 14 | for i = 2:num_cell+1 15 | B_n(1,i) = dV(i)/(exp(dV(i))-1.0); %B(+dV) 16 | B_n(2,i) = B_n(1,i)*exp(dV(i)); %B(-dV) 17 | end 18 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/BernoulliFnc_p.m: -------------------------------------------------------------------------------- 1 | function B_p = BernoulliFnc_p(fullV) 2 | global num_cell phi_a Vt l gap_hole 3 | 4 | dV = zeros(1,num_cell+1); 5 | B_p = zeros(2, num_cell+1); 6 | 7 | for i = 2:num_cell+1 %so dV(100) = dV(101: is at x=L) - dV(100, at x= l-dx). 8 | dV(i) = fullV(i)-fullV(i-1); %these fullV's = V/Vt, when solved poisson. 9 | end 10 | 11 | dV(2) = dV(2) + phi_a/Vt; %injection step at left bndry (holes electrode) 12 | %dV(l+1) = dV(l+1) + gap_hole/Vt; %energy step at interface 13 | 14 | dV(l+1) = dV(l+1) + gap_hole/Vt; 15 | 16 | for i = 2:num_cell+1 17 | B_p(1,i) = dV(i)/(exp(dV(i))-1.0); %B(+dV) 18 | B_p(2,i) = B_p(1,i)*exp(dV(i)); %B(-dV) 19 | end 20 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/CalTrap.m: -------------------------------------------------------------------------------- 1 | function [pT_full, nT_full] = CalTrap(p_full, n_full) %allows to return 2 values with this notation 2 | global H_D H_A N N_HOMO N_LUMO T T_tD T_tA; 3 | 4 | pT_full = H_D*(p_full*N/N_HOMO).^(T/T_tD); %USE .^ for element wise (scalar) power 5 | nT_full = H_A*(n_full*N/N_LUMO).^(T/T_tA); 6 | 7 | pT_full = pT_full/N; 8 | nT_full = nT_full/N; 9 | 10 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/Continuity_n_fnc.m: -------------------------------------------------------------------------------- 1 | function Fn_element = Continuity_n_fnc(Jn_r, Jn_l, Uni) 2 | %Jn_r and Jn_l stand for right and left 3 | global dx q Cn 4 | 5 | Fn_element = (Jn_r - Jn_l) + Cn*Uni; %NOTE: Jn is defined on right side: i.e. Jn_{i-1/2} is Jn_i 6 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/Continuity_p_fnc.m: -------------------------------------------------------------------------------- 1 | function Fp_element = Continuity_p_fnc(Jp_r, Jp_l, Upi) 2 | 3 | global dx q Cp 4 | 5 | Fp_element = (Jp_r - Jp_l) - Cp*Upi; %NOTE: Jn is defined on right side: i.e. Jn_{i-1/2} is Jn_i 6 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/Fu.m: -------------------------------------------------------------------------------- 1 | function Fu = Net_Genfnc(U, k_ppd, zeta, Rtotal) 2 | 3 | %this is treated as a function of U--> from here, the new U is found.. 4 | 5 | global l num_elements 6 | %NOTE: b/c U is only non-zero at 2 points: l and l+1, when count real 7 | %device indices, Fu has non-zero values only at 2 pts: Fu uses interior 8 | %device points: so will be non-zero at l-1 and l (corresponding to l and 9 | %l+1). 10 | 11 | Fu = zeros(1,num_elements); 12 | 13 | Fu(l-1) = U(l-1) - (k_ppd*zeta - Rtotal); %note: k_ppd, zeta and Rtotal are not arrays 14 | Fu(l) = U(l) - (k_ppd*zeta - Rtotal); -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/Net_Genfnc.m: -------------------------------------------------------------------------------- 1 | function Fu = Net_Genfnc(U, k_ppd, zeta, Rtotal) 2 | 3 | %this is treated as a function of U--> from here, the new U is found.. 4 | 5 | global l num_elements 6 | %NOTE: b/c U is only non-zero at 2 points: l and l+1, when count real 7 | %device indices, Fu has non-zero values only at 2 pts: Fu uses interior 8 | %device points: so will be non-zero at l-1 and l (corresponding to l and 9 | %l+1). 10 | 11 | Fu = zeros(1,num_elements); 12 | 13 | Fu(l-1) = U(l-1) - (k_ppd*zeta - Rtotal); %note: k_ppd, zeta and Rtotal are not arrays 14 | Fu(l) = U(l) - (k_ppd*zeta - Rtotal); 15 | 16 | Fu = Fu/10^29; -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/Poissonfnc.m: -------------------------------------------------------------------------------- 1 | function Fv_element = Poissonfnc(fullV, ni, pi,nTi, pTi, i) 2 | %NOTE: define imput argument n(i) = ni --> is more robust... 3 | %since only need to pass 1 n and p value, don't pass the whole array! 4 | %NOTE: need to pass it i, so it knows what element are computing! 5 | 6 | %NOTE: need fullV, b/c need boundary fullV values when do laplacian 7 | 8 | global epsilon q num_elements CV 9 | %NOTE: epsilon = (relative permitivity)*epsilon_0 10 | 11 | 12 | %the function will be defined for INSIDE the device: from cell 2 to 13 | %num_cell --> or since V, n, and, p are already for INSIDE device, for all 14 | %those elements... 15 | 16 | % eps_r = epsilon/epsilon_0; 17 | 18 | 19 | Fv_element = ((fullV(i) -2*fullV(i+1) + fullV(i+2)) - CV*((ni+nTi)-(pi+pTi))); %the fullV values use +1 everywhere, b/c starts from 1 = endpoint, whereas for n and p, 1 = 1st interior point 20 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/R.m: -------------------------------------------------------------------------------- 1 | function R = R(n_full, p_full) 2 | global gap_A gap_D q kb T N N_LUMO N_HOMO l; 3 | 4 | %Calcurate R_SRH with acceptor trap 5 | a = -gap_A*q; 6 | b = kb*T*log(n_full(l+1)*N/N_LUMO); %the log here is an ln 7 | n = 100; %I verified that 100 is enough to give reasonable result for the integration 8 | sumk = 0.0; 9 | 10 | for k= 1:n-1 11 | sumk = sumk + f_dR_SRH_A(a + k*(b-a)/n, n_full, p_full); %the first term here is E in f_dR_SRH_A 12 | end 13 | R_SRH_A = (b-a)/n*(f_dR_SRH_A(a, n_full, p_full)/2.0D0 + sumk + f_dR_SRH_A(b, n_full, p_full)/2.0D0); 14 | 15 | %Calcurate R_SRH with donor trap 16 | a = kb*T*log(p_full(l)*N/N_HOMO); 17 | b = -gap_D*q; 18 | n = 100; 19 | sumk = 0.0D0; 20 | 21 | for k=1:n-1 22 | sumk = sumk + f_dR_SRH_D(a + k*(b-a)/n, n_full, p_full); 23 | end 24 | R_SRH_D = (b-a)/n*(f_dR_SRH_D(a, n_full, p_full)/2.0D0 + sumk + f_dR_SRH_D(b, n_full, p_full)/2.0D0); 25 | 26 | R_SRH = R_SRH_A + R_SRH_D; 27 | R = R_SRH; -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/RecalculateU.m: -------------------------------------------------------------------------------- 1 | function RecalculateU = RecalculateU(V_left,V_right, n_full, p_full) 2 | 3 | global l Vt L_int k_ppr_0 Jx k_ppd_eq manual_Rp E_gap N N_LUMO N_HOMO q Rp_man 4 | 5 | F = (V_right-V_left).*Vt./L_int; %V_left = V(l-1), V_right = V(l) nd recall that V(l) and V(l-1) correspond to fullV(l+1) and fullV(l) 6 | k_ppd = kppd(F); 7 | Rtotal = R(n_full, p_full); %calculates recombination: we use trap and SRH so implement just those 2--> do in sepearte function file 8 | k_ppr = k_ppr_0*exp(-F.*L_int./Vt); %ISSUE IS HERE! 9 | zeta_eq = Rtotal./k_ppd_eq; %use ./ to tell it that these are not matrixes! otherwise get error. 10 | zeta = (Jx./L_int+ k_ppr.*zeta_eq + Rtotal)./(k_ppr+k_ppd); 11 | netU = k_ppd*zeta - Rtotal; 12 | 13 | if(manual_Rp) 14 | temp = ( E_gap - F*L_int+ Vt*log(n_full(l+1)*p_full(l)*N^2/(N_LUMO*N_HOMO)) )/(q*L_int*Rp_man); %IN TEH FIRST TRIAL, N_FULL L+1 IS NOT DEFINED!!! IS JUST = 0!!: NEED TO FIX THIS!! 15 | netU = netU-temp; 16 | end 17 | 18 | RecalculateU = netU; -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/SetAn_val.m: -------------------------------------------------------------------------------- 1 | function An_val = SetAn_val(B_n) 2 | global num_elements n_mob 3 | 4 | An = zeros(num_elements,3); 5 | 6 | %BE CAREFUL!: the setup of the sparse matrix elements is 7 | %non-trivial: last entry of Ap(:,1) 1st entry of 8 | %Ap(:,3) are unused b/c off diagonals have 1 less element than main 9 | %diagonal! 10 | 11 | for i=1:num_elements %I verified that ordering of columns is correct! 12 | An(i,2) = -(n_mob*B_n(1,i+1) + n_mob*B_n(2,i+1+1)); %I HAVE VERIFIED THAT ALL THE dV's properly match up with indices! All the extra +1's are b/c B starts at i=2 (1st pt inside device). 13 | end 14 | for i = 1:num_elements-1 15 | An(i,1) = n_mob*B_n(2,i+1+1); %should be i+1+1 b/c have B(dVi)*p_i-1 (see fortran reindexed version) 16 | end 17 | An(num_elements, 1) = 0; %last element is unused 18 | for i = 2:num_elements 19 | An(i,3) = n_mob*B_n(1,i+1); %1st element here corresponds to the 1st row of matrix: so need to use B3 --> corresponding to p(3) 20 | end 21 | An(1,3) = 0; %1st element of Ap(:,3) is unused 22 | 23 | 24 | An_val = spdiags(An,-1:1,num_elements,num_elements); -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/SetAp_val.m: -------------------------------------------------------------------------------- 1 | function Ap_val = SetAp_val(B_p) 2 | global num_elements p_mob 3 | 4 | Ap = zeros(num_elements,3); 5 | 6 | %BE CAREFUL!: the setup of the sparse matrix elements is 7 | %non-trivial: last entry of Ap(:,1) 1st entry of 8 | %Ap(:,3) are unused b/c off diagonals have 1 less element than main 9 | %diagonal! 10 | 11 | for i=1:num_elements %I verified that ordering of columns is correct! 12 | Ap(i,2) = -(p_mob*B_p(2,i+1) + p_mob*B_p(1,i+1+1)); %I HAVE VERIFIED THAT ALL THE dV's properly match up with indices! All the extra +1's are b/c B starts at i=2 (1st pt inside device). 13 | end 14 | for i = 1:num_elements-1 15 | Ap(i,1) = p_mob*B_p(1,i+1+1); %should be i+1+1 b/c have B(dVi)*p_i-1 (see fortran reindexed version) 16 | end 17 | Ap(num_elements, 1) = 0; %last element is unused 18 | for i = 2:num_elements 19 | Ap(i,3) = p_mob*B_p(2,i+1); %1st element here corresponds to the 1st row of matrix: so need to use B3 --> corresponding to p(3) 20 | end 21 | Ap(1,3) = 0; %1st element of Ap(:,3) is unused 22 | 23 | 24 | Ap_val = spdiags(Ap,-1:1,num_elements,num_elements); -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/Setbn.m: -------------------------------------------------------------------------------- 1 | function bn = Setbn(B_n, n_full, Un) 2 | global l Cn_gummel num_cell num_elements n_mob; 3 | 4 | bn = zeros(num_elements,1); 5 | 6 | %enforce boundary conditions through bp 7 | bn(1) = -n_mob*B_n(2,2)*n_full(1); 8 | bn(num_elements) = -n_mob*B_n(1,num_cell+1)*n_full(num_cell+1); %THERE WAS A MISTAKE HERE!! HAD num_elements +1 instead of num_cell+1! so weren't using the electron injection barrier! b/c num_elements +1 = num_cell 9 | 10 | %introduce a net generation rate at 1 mesh point to the right of where 11 | %introduce the bp generation rate 12 | bn(l) = -Cn_gummel*Un; 13 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/Setbp.m: -------------------------------------------------------------------------------- 1 | function bp = Setbp(B_p, p_full, Up) 2 | global l Cp_gummel num_cell num_elements p_mob; 3 | 4 | bp = zeros(num_elements,1); 5 | 6 | %enforce boundary conditions through bp 7 | bp(1,1) = -p_mob*B_p(1,2)*p_full(1); 8 | bp(num_elements, 1) = -p_mob*B_p(2,num_cell+1)*p_full(num_cell+1); %ENFORCE RIGHT SIDE P IS = 0 9 | 10 | %introduce a net generation rate somewhere in the middle 11 | bp(l-1) = -Cp_gummel*Up; -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/dBni_dVi.m: -------------------------------------------------------------------------------- 1 | %dBn(dVi)/dVi 2 | function dBni_dVi = dBni_dVi(fullV) 3 | 4 | global phi_c gap_elec Vt num_cell l 5 | 6 | dV = zeros(1,num_cell+1); 7 | 8 | for i = 2:num_cell+1 %so dV(100) = dV(101: is at x=L) - dV(100, at x= l-dx). 9 | dV(i) = fullV(i)-fullV(i-1); %these fullV's ARE ACTUALLY PSI PRIME; ALREADY = V/Vt, when solved poisson. 10 | end 11 | 12 | dV(num_cell+1) = dV(num_cell+1) + phi_c/Vt; %injection step at right bndry (electrons electrode) 13 | dV(l+1) = dV(l+1) + gap_elec/Vt; %energy step at interface 14 | 15 | %recall that fullV is already scaled by Vt 16 | for i = 2:num_cell+1 17 | exp_term = exp(dV(i)) - 1; 18 | dBni_dVi(i) = (exp_term - exp(dV(i))*dV(i))/(exp_term)^2; 19 | end -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/dBni_dVi_min1.m: -------------------------------------------------------------------------------- 1 | %dBn(dVi)/dV_i-1 2 | function dBni_dVi_min1 = dBni_dVi_min1(fullV) 3 | 4 | global phi_c gap_elec Vt num_cell l 5 | 6 | dV = zeros(1,num_cell+1); 7 | 8 | for i = 2:num_cell+1 %so dV(100) = dV(101: is at x=L) - dV(100, at x= l-dx). 9 | dV(i) = fullV(i)-fullV(i-1); %these fullV's ARE ACTUALLY PSI PRIME; ALREADY = V/Vt, when solved poisson. 10 | end 11 | 12 | dV(num_cell+1) = dV(num_cell+1) + phi_c/Vt; %injection step at right bndry (electrons electrode) 13 | dV(l+1) = dV(l+1) + gap_elec/Vt; %energy step at interface 14 | 15 | %recall that fullV is already scaled by Vt 16 | for i = 2:num_cell+1 17 | exp_term = exp(dV(i)); 18 | dBni_dVi_min1(i) = (exp_term*dV(i) - (exp(dV(i))-1))/(exp_term-1)^2; 19 | end -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/dBpi_dVi.m: -------------------------------------------------------------------------------- 1 | %dBn(dVi)/dVi 2 | function dBpi_dVi = dBpi_dVi(fullV) 3 | 4 | global phi_a gap_hole Vt num_cell l 5 | 6 | dV = zeros(1,num_cell+1); 7 | 8 | for i = 2:num_cell+1 %so dV(100) = dV(101: is at x=L) - dV(100, at x= l-dx). 9 | dV(i) = fullV(i)-fullV(i-1); %these fullV's ARE ACTUALLY PSI PRIME; ALREADY = V/Vt, when solved poisson. 10 | end 11 | 12 | dV(2) = dV(2) + phi_a/Vt; %injection step at left bndry (holes electrode) 13 | dV(l+1) = dV(l+1) + gap_hole/Vt; 14 | 15 | %recall that fullV is already scaled by Vt 16 | for i = 2:num_cell+1 17 | exp_term = exp(dV(i)) - 1; 18 | dBpi_dVi(i) = (exp_term - exp(dV(i))*dV(i))/(exp_term)^2; 19 | end -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/dBpi_dVi_min1.m: -------------------------------------------------------------------------------- 1 | %dBp(dVi)/dV_i-1 2 | function dBpi_dVi_min1 = dBpi_dVi_min1(fullV) 3 | 4 | global phi_a gap_hole Vt num_cell l 5 | 6 | dV = zeros(1,num_cell+1); 7 | 8 | for i = 2:num_cell+1 %so dV(100) = dV(101: is at x=L) - dV(100, at x= l-dx). 9 | dV(i) = fullV(i)-fullV(i-1); %these fullV's ARE ACTUALLY PSI PRIME; ALREADY = V/Vt, when solved poisson. 10 | end 11 | 12 | dV(2) = dV(2) + phi_a/Vt; %injection step at left bndry (holes electrode) 13 | dV(l+1) = dV(l+1) + gap_hole/Vt; 14 | 15 | %recall that fullV is already scaled by Vt 16 | for i = 2:num_cell+1 17 | exp_term = exp(dV(i)); 18 | dBpi_dVi_min1(i) = (exp_term*dV(i) - (exp(dV(i))-1))/(exp_term-1)^2; 19 | end -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/dFn_dV.m: -------------------------------------------------------------------------------- 1 | function dFn_dV = dFn_dV(fullV, n_full, eps_V, Un) 2 | 3 | global num_cell num_elements n_mob 4 | %this will call Bernoulli, and recalculate Jn 5 | %Will recalculate entire array--> using 1 eps_n value... 6 | 7 | %compute fnc values to right and left of the node's V value... 8 | B_n_plus = BernoulliFnc_n(fullV + eps_V/2.); 9 | B_n_minus = BernoulliFnc_n(fullV - eps_V/2.); 10 | 11 | % Calculate drift diffusion J's 12 | % Use the SG definition--> BUT COEFFS go into Cn and Cp... 13 | for i = 1:num_cell %verified that this is right 14 | Jn_plus(1,i+1) = n_mob*(n_full(i+1)*B_n_plus(1,i+1)-n_full(i)*B_n_plus(2,i+1)); 15 | Jn_minus(1,i+1) = n_mob*(n_full(i+1)*B_n_minus(1,i+1)-n_full(i)*B_n_minus(2,i+1)); 16 | end 17 | 18 | for i = 1:num_elements %so now this is recalculating continuity function, using the shifted V values 19 | dFn_dV(i) = (Continuity_n_fnc(Jn_plus(1,i+2), Jn_plus(1,i+1), Un(i)) - Continuity_n_fnc(Jn_minus(1,i+2), Jn_minus(1,i+1), Un(i)))/eps_V; 20 | end 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/dFn_dn.m: -------------------------------------------------------------------------------- 1 | function dFn_dn = dFn_dn(B_n, n_full, Un, eps_n) 2 | 3 | global num_cell n_mob num_elements 4 | %here don't need to recalculate Bernoulli's b/c they are independent of n 5 | 6 | for i = 1:num_cell %verified that this is right 7 | %now plus and minus is determined by shifting n_full values... 8 | Jn_plus(1,i+1) = n_mob*((n_full(i+1) + eps_n/2.)*B_n(1,i+1)-(n_full(i)+ eps_n/2.)*B_n(2,i+1)); 9 | Jn_minus(1,i+1) = n_mob*((n_full(i+1) - eps_n/2.)*B_n(1,i+1)-(n_full(i)- eps_n/2.)*B_n(2,i+1)); 10 | end 11 | 12 | for i = 1:num_elements %so now this is recalculating continuity function, using the shifted V values 13 | dFn_dn(i) = (Continuity_n_fnc(Jn_plus(1,i+2), Jn_plus(1,i+1), Un(i)) - Continuity_n_fnc(Jn_minus(1,i+2), Jn_minus(1,i+1), Un(i)))/eps_n; 14 | end 15 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/dFp_dV.m: -------------------------------------------------------------------------------- 1 | function dFp_dV = dFp_dV(fullV, p_full, eps_V, Up) 2 | 3 | global num_cell num_elements p_mob 4 | %this will call Bernoulli, and recalculate Jn 5 | %Will recalculate entire array--> using 1 eps_n value... 6 | 7 | %compute fnc values to right and left of the node's V value... 8 | B_p_plus = BernoulliFnc_p(fullV + eps_V/2.); 9 | B_p_minus = BernoulliFnc_p(fullV - eps_V/2.); 10 | 11 | % Calculate drift diffusion J's 12 | % Use the SG definition--> but coeffs go into Cn and Cp... 13 | for i = 1:num_cell %verified that this is right 14 | Jp_plus(1,i+1) = -p_mob*(p_full(i+1)*B_p_plus(1,i+1)-p_full(i)*B_p_plus(2,i+1)); 15 | Jp_minus(1,i+1) = -p_mob*(p_full(i+1)*B_p_minus(1,i+1)-p_full(i)*B_p_minus(2,i+1)); 16 | end 17 | 18 | for i = 1:num_elements %so now this is recalculating continuity function, using the shifted V values 19 | dFp_dV(i) = (Continuity_p_fnc(Jp_plus(1,i+2), Jp_plus(1,i+1), Up(i)) - Continuity_p_fnc(Jp_minus(1,i+2), Jp_minus(1,i+1), Up(i)))/eps_V; 20 | end 21 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/dFp_dp.m: -------------------------------------------------------------------------------- 1 | function dFp_dp = dFp_dp(B_p, p_full, Up, eps_p) 2 | 3 | global num_cell p_mob num_elements 4 | %here don't need to recalculate Bernoulli's b/c they are independent of n 5 | 6 | for i = 1:num_cell %verified that this is right 7 | %now plus and minus is determined by shifting n_full values... 8 | Jp_plus(1,i+1) = p_mob*((p_full(i+1) + eps_p/2.)*B_p(1,i+1)-(p_full(i)+ eps_p/2.)*B_p(2,i+1)); 9 | Jp_minus(1,i+1) = p_mob*((p_full(i+1) - eps_p/2.)*B_p(1,i+1)-(p_full(i)- eps_p/2.)*B_p(2,i+1)); 10 | end 11 | 12 | for i = 1:num_elements %so now this is recalculating continuity function, using the shifted V values 13 | dFp_dp(i) = (Continuity_p_fnc(Jp_plus(1,i+2), Jp_plus(1,i+1), Up(i)) - Continuity_p_fnc(Jp_minus(1,i+2), Jp_minus(1,i+1), Up(i)))/eps_p; 14 | end -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/dn_dx.m: -------------------------------------------------------------------------------- 1 | function dn_dx = dn_dx(n_full) 2 | global dx num_elements 3 | 4 | for i = 2:num_elements+1 %shift by 1, b/c boundary values of n_full start at 1 5 | dn_dx(i-1) = (n_full(i+1) - n_full(i-1))/(2*dx); %shift to i-1 b/c dn_dx defines inside values only 6 | end -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/dnegBni_dVi.m: -------------------------------------------------------------------------------- 1 | %dBn(-dVi)/dVi 2 | function dnegBni_dVi = dnegBni_dVi(fullV) 3 | global phi_c gap_elec Vt num_cell l 4 | 5 | dV = zeros(1,num_cell+1); 6 | 7 | for i = 2:num_cell+1 %so dV(100) = dV(101: is at x=L) - dV(100, at x= l-dx). 8 | dV(i) = fullV(i)-fullV(i-1); %these fullV's ARE ACTUALLY PSI PRIME; ALREADY = V/Vt, when solved poisson. 9 | end 10 | 11 | dV(num_cell+1) = dV(num_cell+1) + phi_c/Vt; %injection step at right bndry (electrons electrode) 12 | dV(l+1) = dV(l+1) + gap_elec/Vt; %energy step at interface 13 | 14 | for i = 2:num_cell +1 15 | exp_term = exp(dV(i)); 16 | dnegBni_dVi(i) = exp_term*(-dV(i) + exp_term - 1)/(exp_term - 1)^2; 17 | end -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/dnegBni_dVi_min1.m: -------------------------------------------------------------------------------- 1 | %dBn(-dVi)/dV_i-1 2 | function dnegBni_dVi_min1 = dnegBni_dVi_min1(fullV) 3 | global phi_c gap_elec Vt num_cell l 4 | 5 | dV = zeros(1,num_cell+1); 6 | 7 | for i = 2:num_cell+1 %so dV(100) = dV(101: is at x=L) - dV(100, at x= l-dx). 8 | dV(i) = fullV(i)-fullV(i-1); %these fullV's ARE ACTUALLY PSI PRIME; ALREADY = V/Vt, when solved poisson. 9 | end 10 | 11 | dV(num_cell+1) = dV(num_cell+1) + phi_c/Vt; %injection step at right bndry (electrons electrode) 12 | dV(l+1) = dV(l+1) + gap_elec/Vt; %energy step at interface 13 | 14 | %recall that fullV is already scaled by Vt 15 | for i = 2:num_cell+1 16 | exp_term = exp(-dV(i)); 17 | dnegBni_dVi_min1(i) = (exp_term - 1 - exp_term*(-dV(i)))/(exp_term -1)^2; 18 | end -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/dnegBpi_dVi.m: -------------------------------------------------------------------------------- 1 | %dBp(-dVi)/dVi 2 | function dnegBpi_dVi = dnegBpi_dVi(fullV) 3 | global phi_a gap_hole Vt num_cell l 4 | 5 | dV = zeros(1,num_cell+1); 6 | 7 | for i = 2:num_cell+1 %so dV(100) = dV(101: is at x=L) - dV(100, at x= l-dx). 8 | dV(i) = fullV(i)-fullV(i-1); %these fullV's = V/Vt, when solved poisson. 9 | end 10 | 11 | dV(2) = dV(2) + phi_a/Vt; %injection step at left bndry (holes electrode) 12 | dV(l+1) = dV(l+1) + gap_hole/Vt; 13 | 14 | for i = 2:num_cell +1 15 | exp_term = exp(dV(i)); 16 | dnegBpi_dVi(i) = exp_term*(-dV(i) + exp_term - 1)/(exp_term - 1)^2; 17 | end -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/dnegBpi_dVi_min1.m: -------------------------------------------------------------------------------- 1 | %dBp(-dVi)/dV_i-1 2 | function dnegBpi_dVi_min1 = dnegBpi_dVi_min1(fullV) 3 | global phi_a gap_hole Vt num_cell l 4 | 5 | dV = zeros(1,num_cell+1); 6 | 7 | for i = 2:num_cell+1 %so dV(100) = dV(101: is at x=L) - dV(100, at x= l-dx). 8 | dV(i) = fullV(i)-fullV(i-1); %these fullV's = V/Vt, when solved poisson. 9 | end 10 | 11 | dV(2) = dV(2) + phi_a/Vt; %injection step at left bndry (holes electrode) 12 | dV(l+1) = dV(l+1) + gap_hole/Vt; 13 | 14 | %recall that fullV is already scaled by Vt 15 | for i = 2:num_cell+1 16 | exp_term = exp(-dV(i)); 17 | dnegBpi_dVi_min1(i) = (exp_term - 1 - exp_term*(-dV(i)))/(exp_term -1)^2; 18 | end -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/dp_dx.m: -------------------------------------------------------------------------------- 1 | function dp_dx = dp_dx(p_full) 2 | global dx num_elements 3 | 4 | for i = 2:num_elements+1 %shift by 1, b/c boundary values of n_full start at 1 5 | dp_dx(i-1) = (p_full(i+1) - p_full(i-1))/(2*dx); %shift to i-1 b/c dn_dx defines inside values only 6 | end 7 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/f_dR_SRH_A.m: -------------------------------------------------------------------------------- 1 | function f_dR_SRH_A = f_dR_SRH_A(E, n_full, p_full) 2 | global gap_A q kb T T_tA cap_n cap_p N_LUMO N_HOMO Vt l N H_A 3 | 4 | n1A = N_LUMO*exp(E/(kb*T)); 5 | p1A = N_HOMO*exp((-gap_A*q-E)/(kb*T)); 6 | f_dR_SRH_A = H_A/(kb*T_tA)*exp(E/(kb*T_tA))*cap_n*cap_p*(n_full(l+1)*p_full(l)*N^2-N_LUMO*N_HOMO*exp(-gap_A/Vt))/(cap_n*(n_full(l+1)*N + n1A) + cap_p*(p_full(l)*N + p1A)); -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/f_dR_SRH_D.m: -------------------------------------------------------------------------------- 1 | function f_dR_SRH_D = f_dR_SRH_D(E, n_full, p_full) 2 | global gap_D q kb T T_tD cap_n cap_p N_LUMO N_HOMO Vt l N H_D 3 | 4 | 5 | n1D = N_LUMO*exp((-gap_D*q-E)/(kb*T)); 6 | p1D = N_HOMO*exp(E/(kb*T)); 7 | f_dR_SRH_D = -H_D/(kb*T_tD)*exp(E/(kb*T_tD))*cap_n*cap_p*(n_full(l+1)*p_full(l)*N^2 - N_LUMO*N_HOMO*exp(-gap_D/Vt))/(cap_n*(n_full(l+1)*N + n1D) + cap_p*(p_full(l)*N + p1D)); -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/f_of_zeta.m: -------------------------------------------------------------------------------- 1 | function fnc_of_zeta = f_of_zeta(zeta, F) %make returned value have diff name than fnc, to not have confusion 2 | global L_int k_rec Eb rc Vt 3 | fnc_of_zeta = 3.0D0./(4.0D0.*pi.*L_int^3).*k_rec.*exp((-Eb + F.*rc.*cos(zeta))./Vt); %the integrand -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/kppd.m: -------------------------------------------------------------------------------- 1 | function k_ppd = kppd(F) 2 | global q epsilon Vt Eb k_rec L_int 3 | 4 | 5 | if(F <= 0.0D0) %This performs the integral over half sphere for case when field opposes dissociation 6 | zeta1 = -pi/2.0D0; 7 | zeta2 = pi/2.0D0; 8 | n = 1000; 9 | sumk = 0.0D0; 10 | for k=1:n-1 11 | sumk = sumk + f_of_zeta(zeta1 + k*(zeta2-zeta1)/n, F); 12 | end 13 | k_ppd = (zeta2-zeta1)./n.*(f_of_zeta(zeta1, F)./2.0D0 + sumk + f_of_zeta(zeta1, F)./2.0D0); 14 | k_ppd = k_ppd./pi; 15 | else 16 | 17 | bk = q*F/(8*pi*epsilon*(Vt^2)); %b in Bessel fnc. experssion (**2 means ^2) %USE ./ to tell matlab that its element by element operation --> BUT THIS THEN RETURNS AN EMPTY MATRIX! 18 | k_ppd = (3.0.*k_rec./(4.0*pi.*L_int^3))*exp(-Eb./Vt)*(1.0 + bk + (bk^2)/3.0 + (bk^3)/18.0 + (bk^4)/180.0); %Bessel function is expanded to 4th order here 19 | end 20 | 21 | %not sure what these are for 22 | %bk = q*F/(8.0*pi*epsilon*Vt^2) 23 | %k_ppd2 = (3.0*k_rec/(4.0*pi*L_int^3))*exp(-Eb/Vt)*(1.0 + bk + (bk^2)/3.0 + (bk^3)/18.0D0 + (bk^4)/180.0D0); %Bessel function is expanded to 4th order here -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/nTraps_fnc.m: -------------------------------------------------------------------------------- 1 | function FnT_element = nTraps_fnc(ni, nTi) 2 | 3 | global H_A T T_tA N N_LUMO 4 | 5 | FnT_element = nTi - (H_A/N)*(ni*N/N_LUMO).^(T/T_tA); %will be array of num_elements length 6 | 7 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Organic_bilayer_solar_cell/Newton-Raphson_method/pTraps_fnc.m: -------------------------------------------------------------------------------- 1 | function FpT_element = pTraps_fnc(pi, pTi) 2 | 3 | global H_D T T_tD N N_HOMO 4 | 5 | FpT_element = pTi - (H_D/N)*(pi*N/N_HOMO).^(T/T_tD); %will be array of num_elements length -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Perovskite_solar_cell/BernoulliFnc_n.m: -------------------------------------------------------------------------------- 1 | % This is the Bernoulli function for electrons from Scharfetter-Gummel discretization 2 | function B_n = BernoulliFnc_n(fullV) 3 | global num_cell Vt ETL_int_CBstep l_ETL_int HTL_int_CBstep l_HTL_int BCP_int_CBstep l_BCP_int 4 | 5 | dV = zeros(1,num_cell+1); 6 | B_n = zeros(2, num_cell+1); 7 | 8 | for i = 2:num_cell+1 9 | dV(i) = fullV(i)-fullV(i-1); 10 | end 11 | 12 | % energy barriers 13 | % injection barriers at contacts should not be incuded b/c are already in 14 | % the BCs 15 | dV(l_HTL_int +1) = dV(l_HTL_int +1) + HTL_int_CBstep/Vt; 16 | dV(l_BCP_int+1) = dV(l_BCP_int+1) + BCP_int_CBstep/Vt; 17 | dV(l_ETL_int +1) = dV(l_ETL_int +1) + ETL_int_CBstep/Vt; 18 | 19 | for i = 2:num_cell+1 20 | B_n(1,i) = dV(i)/(exp(dV(i))-1.0); % B(+dV) 21 | B_n(2,i) = B_n(1,i)*exp(dV(i)); % B(-dV) 22 | end 23 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Perovskite_solar_cell/BernoulliFnc_p.m: -------------------------------------------------------------------------------- 1 | % This is the Bernoulli function for holes from Scharfetter-Gummel discretization 2 | function B_p = BernoulliFnc_p(fullV) 3 | global num_cell l_HTL_int Vt HTL_int_VBstep l_ETL_int ETL_int_VBstep BCP_int_VBstep l_BCP_int 4 | 5 | dV = zeros(1,num_cell+1); 6 | B_p = zeros(2, num_cell+1); 7 | 8 | for i = 2:num_cell+1 9 | dV(i) = fullV(i)-fullV(i-1); 10 | end 11 | 12 | % energy barriers 13 | % injection barriers at contacts should not be incuded b/c are already in 14 | % the BCs 15 | dV(l_HTL_int +1 ) = dV(l_HTL_int +1) + HTL_int_VBstep/Vt; 16 | dV(l_ETL_int +1) = dV(l_ETL_int +1) + ETL_int_VBstep/Vt; 17 | dV(l_BCP_int+1) = dV(l_BCP_int+1) + BCP_int_VBstep/Vt; 18 | 19 | for i = 2:num_cell+1 20 | B_p(1,i) = dV(i)/(exp(dV(i))-1.0); % B(+dV) 21 | B_p(2,i) = B_p(1,i)*exp(dV(i)); % B(-dV) 22 | end 23 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Perovskite_solar_cell/GenerationRate.m: -------------------------------------------------------------------------------- 1 | function G = GenerationRate(gen_rate_input) 2 | global l_HTL_int l_ETL_int num_cell G_max 3 | 4 | % This uses real generation rate from optical model. 5 | % Reads in data from file gen_rate.txt (must be in same directory) 6 | % gen_rate.txt contains generation rate at each mesh grid point in the 7 | % simulation. The # of points in the file must match the mesh 8 | % used in the code. 9 | 10 | G(1:l_HTL_int) = 0; % no charge generation in HTL 11 | 12 | G(l_HTL_int+1:l_ETL_int) = gen_rate_input; 13 | 14 | %normalize by the max value 15 | G(l_HTL_int+1:l_ETL_int) = G(l_HTL_int+1:l_ETL_int)./max(G(l_HTL_int+1:l_ETL_int)); 16 | 17 | G(l_ETL_int+1:num_cell+1) = 0; % no charge generation in ETL 18 | 19 | G = G_max*G(2:num_cell); %skip the endpoints b/c we don't need thme in Un.... 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Perovskite_solar_cell/README.md: -------------------------------------------------------------------------------- 1 | # 1D Drift Diffusion for perovskite device 2 | 3 | * This solves the drift-diffusion equations for a perovskite device with structure: 4 | PEDOT:PSS/perovskite/C60/BCP. 5 | * It includes the effects of Langevin and Shockley-Read-Hall (SRH) trap-assisted recombination 6 | * Photogeneration rate is specified through an input file: gen_rate.txt. The generation rate 7 | can be calculated using an optical transfer matrix approach. A sample file is included. 8 | 9 | This code was used to model experimental perovskite devices and results were published in: 10 | 11 | T. Golubev, D. Liu, R. Lunt, P. Duxbury. Understanding the impact of C60 at the interface of perovskite solar cells via drift-diffusion modeling. AIP Advances 9, 035026 (2019) 12 | https://aip.scitation.org/doi/full/10.1063/1.5068690 13 | 14 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Perovskite_solar_cell/R_Langevin.m: -------------------------------------------------------------------------------- 1 | function R_Langevin_array = R_Langevin(n_full, p_full) 2 | global k_rec n1 p1 l_HTL_int l_ETL_int num_cell N 3 | 4 | R_Langevin_array = zeros(num_cell+1,1); 5 | 6 | for i = l_HTL_int +1:l_ETL_int %for within pervoskite layer: layer starts at l_HTL_+1.... 7 | R_Langevin_array(i) = k_rec*(N*n_full(i)*N*p_full(i) - n1*p1); 8 | end 9 | 10 | R_Langevin_array = R_Langevin_array(2:num_cell); % to make sure aligned with G 11 | 12 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Perovskite_solar_cell/R_SRH_ETL.m: -------------------------------------------------------------------------------- 1 | function R_SRH_ETL_array = R_SRH_ETL(n_full, p_full) 2 | global ETL_traps num_elements l_ETL_int L_int_eachside cap_n cap_p n1 p1 N L_int; 3 | 4 | R_SRH_ETL_array = zeros(num_elements,1); 5 | 6 | for i = l_ETL_int-9:l_ETL_int 7 | R_SRH_ETL_array(i) = cap_n*cap_p*ETL_traps*(N*n_full(i)*N*p_full(i) - n1*p1)/(cap_n*(N*n_full(i) + n1) + (cap_p*(N*p_full(i) + p1))); 8 | if(R_SRH_ETL_array(i,1) < 0.0) 9 | R_SRH_ETL_array(i,1) = 0.0; % negative recombination rate is not physical 10 | end 11 | end 12 | 13 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Perovskite_solar_cell/R_SRH_HTL.m: -------------------------------------------------------------------------------- 1 | function R_SRH_HTL_array = R_SRH_HTL(n_full, p_full) 2 | global HTL_traps num_elements l_HTL_int cap_n cap_p n1 p1 N L_int L_int_eachside; 3 | 4 | R_SRH_HTL_array = zeros(num_elements,1); 5 | 6 | for i = l_HTL_int+1:l_HTL_int+10 7 | R_SRH_HTL_array(i,1) = cap_n*cap_p*HTL_traps*(N*n_full(i).*N*p_full(i) - n1*p1)/(cap_n*(N*n_full(i) + n1) + (cap_p*(N*p_full(i) + p1))); 8 | if(R_SRH_HTL_array(i,1) < 0.0) 9 | R_SRH_HTL_array(i,1) = 0.0; % negative recombination rate is not physical 10 | end 11 | end 12 | 13 | 14 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Perovskite_solar_cell/SetAV_val.m: -------------------------------------------------------------------------------- 1 | function AV_val = SetAV_val(epsilon) 2 | global num_elements nx l_HTL_int l_ETL_int l_BCP_int 3 | 4 | AV = zeros(nx-2,3); 5 | 6 | for i=1:num_elements 7 | AV(i,2) = -2.*epsilon(i+1); % i+1 b/c we fill matrix only corresponding to elements INSIDE the device 8 | end 9 | 10 | for i = 1:num_elements-1 11 | AV(i,1) = epsilon(i+1); % 1st element here corresponds to 2nd row of matrix 12 | end 13 | AV(num_elements, 1) = 0; % last element is unused 14 | 15 | 16 | for i = 2:num_elements 17 | AV(i,3) = epsilon(i+1); %1st element here corresponds to the 1st row of matrix 18 | end 19 | 20 | % Rows in the matrix corresponding to interfaces between layers need 21 | % special treatement. When we discretize over the interface, 22 | % we need to use the appropriate epsilon value 23 | AV(l_HTL_int,3) = (epsilon(1)); 24 | AV(l_HTL_int,2) = -(epsilon(floor(num_elements/2)) + epsilon(1)); 25 | 26 | AV(l_ETL_int,3) = (epsilon(floor(num_elements/2))); 27 | AV(l_ETL_int ,2) = -(epsilon(l_ETL_int+1) + epsilon(floor(num_elements/2))); 28 | 29 | AV(l_BCP_int,3) = (epsilon(l_ETL_int+1)); 30 | AV(l_BCP_int,2) = -(epsilon(num_elements) + epsilon(l_ETL_int +1)); 31 | 32 | AV(1,3) = 0; % 1st element of Ap(:,3) is unused 33 | 34 | AV_val = spdiags(AV,-1:1,num_elements,num_elements); -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Perovskite_solar_cell/SetAn_val.m: -------------------------------------------------------------------------------- 1 | function An_val = SetAn_val(B_n) 2 | global num_elements n_mob 3 | 4 | An = zeros(num_elements,3); 5 | 6 | for i=1:num_elements 7 | An(i,2) = -(n_mob(i+1)*B_n(1,i+1) + n_mob(i+1+1)*B_n(2,i+1+1)); 8 | end 9 | for i = 1:num_elements-1 10 | An(i,1) = n_mob(i+1+1)*B_n(2,i+1+1); 11 | end 12 | An(num_elements, 1) = 0; % last element is unused 13 | for i = 2:num_elements 14 | An(i,3) = n_mob(i+1)*B_n(1,i+1); 15 | end 16 | An(1,3) = 0; % 1st element of An(:,3) is unused 17 | 18 | 19 | An_val = spdiags(An,-1:1,num_elements,num_elements); 20 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Perovskite_solar_cell/SetAp_val.m: -------------------------------------------------------------------------------- 1 | function Ap_val = SetAp_val(B_p) 2 | global num_elements p_mob 3 | 4 | Ap = zeros(num_elements,3); 5 | 6 | for i=1:num_elements 7 | Ap(i,2) = -(p_mob(i+1)*B_p(2,i+1) + p_mob(i+1+1)*B_p(1,i+1+1)); 8 | end 9 | 10 | for i = 1:num_elements-1 11 | Ap(i,1) = p_mob(i+1+1)*B_p(1,i+1+1); 12 | end 13 | Ap(num_elements, 1) = 0; % last element is unused 14 | for i = 2:num_elements 15 | Ap(i,3) = p_mob(i+1)*B_p(2,i+1); 16 | end 17 | 18 | Ap(1,3) = 0; % 1st element of Ap(:,3) is unused 19 | 20 | Ap_val = spdiags(Ap,-1:1,num_elements,num_elements); 21 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Perovskite_solar_cell/Setbn.m: -------------------------------------------------------------------------------- 1 | % RHS of matrix equation for electron charge density 2 | function bn = Setbn(B_n, n_full, Un) 3 | global Cn num_cell num_elements n_mob; 4 | 5 | bn = -Cn*Un; 6 | 7 | %enforce boundary conditions through bn 8 | bn(1,1) = bn(1,1) - n_mob(2)*B_n(2,2)*n_full(1); 9 | bn(num_elements,1) = bn(num_elements,1) - n_mob(num_cell +1)*B_n(1,num_cell+1)*n_full(num_cell+1); 10 | 11 | 12 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Perovskite_solar_cell/Setbp.m: -------------------------------------------------------------------------------- 1 | % RHS of matrix equation for hole charge density 2 | function bp = Setbp(B_p, p_full, Up) 3 | global Cp num_cell num_elements p_mob; 4 | 5 | bp = -Cp*Up; 6 | 7 | %enforce boundary conditions for hole charge densities through bp 8 | bp(1,1) = bp(1,1) - p_mob(2)*B_p(1,2)*p_full(1); 9 | bp(num_elements, 1) = bp(num_elements,1) - p_mob(num_cell+1)*B_p(2,num_cell+1)*p_full(num_cell+1); 10 | 11 | -------------------------------------------------------------------------------- /1D/Multi-Layer_Devices/Perovskite_solar_cell/plot_JVs.m: -------------------------------------------------------------------------------- 1 | % allows to plot multiply JV files on 1 graph 2 | % In the pop-up dialog, select the JV text files that want to plot 3 | 4 | clear all 5 | 6 | [File,Path]=uigetfile('*.txt','MultiSelect','on'); 7 | 8 | N = numel(File); 9 | 10 | figure; 11 | 12 | for i = 1:N 13 | 14 | name= File(1,i); 15 | str=sprintf('%s', [Path name{1}]); 16 | format shortG 17 | 18 | data = importdata(str); 19 | 20 | V(:,i) = data(:,1); 21 | J(:,i) = data(:,2)/10; %the /10 is to convert A/m^2 to mA/cm^2 22 | 23 | for j = 1:size(J,1) 24 | JV(j) = 10*J(j,i)*V(j,i); %i is defining WHICH JV curve are working with 25 | end 26 | [maxPower, I] = min(JV) % use min--> b/c in solar cell working regime, my J/s are defined as negative, 27 | % I is the index where the min occurs 28 | 29 | maxPower = abs(maxPower) 30 | %max power points 31 | Jmpp = J(I,i) 32 | Vmpp = V(I,i) 33 | 34 | 35 | %incident power is considered to be 1000 W/m^2 ? 36 | PCE = (maxPower/1000)*100 37 | 38 | plot(V(:,i),J(:,i), 'LineWidth',1.5); 39 | hold on 40 | end 41 | matlab.graphics.internal.setPrintPreferences('DefaultPaperPositionMode','manual') 42 | set(gca, 'FontSize', 24) 43 | xlabel('Voltage (V)','interpreter','latex','FontSize',26.4); 44 | ylabel({'Current Density ($mA/cm^2$)'},'interpreter','latex','FontSize',26.4); 45 | axis([0 1.22 -22 2]); 46 | 47 | 48 | -------------------------------------------------------------------------------- /1D/README.md: -------------------------------------------------------------------------------- 1 | # Drift-Diffusion_models 2 | 3 | 1D Drift Diffusion 4 | 5 | The Drift-Diffusion folder contains codes for solving the Poisson-Drift-Diffusion equations for 6 | single layer and multi-layer devices. -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Scharfetter-Gummel/U_10_32_new/1p1V_Jp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Scharfetter-Gummel/U_10_32_new/1p1V_Jp.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Scharfetter-Gummel/U_10_32_new/1p1V_V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Scharfetter-Gummel/U_10_32_new/1p1V_V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Scharfetter-Gummel/U_10_32_new/1pt1V_E.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Scharfetter-Gummel/U_10_32_new/1pt1V_E.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Scharfetter-Gummel/U_10_32_new/1pt1V_p.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Scharfetter-Gummel/U_10_32_new/1pt1V_p.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Scharfetter-Gummel/U_negative10e30_new/1p11_V_Jp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Scharfetter-Gummel/U_negative10e30_new/1p11_V_Jp.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Scharfetter-Gummel/U_negative10e30_new/1p1V_p.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Scharfetter-Gummel/U_negative10e30_new/1p1V_p.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Scharfetter-Gummel/U_negative10e30_new/1pt1V_E.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Scharfetter-Gummel/U_negative10e30_new/1pt1V_E.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Scharfetter-Gummel/U_negative10e30_new/1pt1V_V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Scharfetter-Gummel/U_negative10e30_new/1pt1V_V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Scharfetter-Gummel/no_generation/E_1V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Scharfetter-Gummel/no_generation/E_1V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Scharfetter-Gummel/no_generation/V_1V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Scharfetter-Gummel/no_generation/V_1V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Scharfetter-Gummel/no_generation/convergence 1V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Scharfetter-Gummel/no_generation/convergence 1V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Scharfetter-Gummel/no_generation/p_1V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Scharfetter-Gummel/no_generation/p_1V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_10e30/E_1V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_10e30/E_1V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_10e30/V_1V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_10e30/V_1V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_10e30/p_1V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_10e30/p_1V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_10e32/E_10V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_10e32/E_10V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_10e32/E_1V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_10e32/E_1V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_10e32/E_20.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_10e32/E_20.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_10e32/V_10V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_10e32/V_10V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_10e32/V_1V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_10e32/V_1V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_10e32/V_20V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_10e32/V_20V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_10e32/convergence_1V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_10e32/convergence_1V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_10e32/p_10V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_10e32/p_10V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_10e32/p_1V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_10e32/p_1V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_10e32/p_20V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_10e32/p_20V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_negative10e30/E_1V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_negative10e30/E_1V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_negative10e30/V_1V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_negative10e30/V_1V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_negative10e30/p_1V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/U_negative10e30/p_1V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/no_generation/E_1V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/no_generation/E_1V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/no_generation/V_1V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/no_generation/V_1V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/no_generation/convergence_1V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/no_generation/convergence_1V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/no_generation/p_1V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/Benchmarks/Slotboom/no_generation/p_1V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/src/Scharfetter-Gummel/BernoulliFnc.m: -------------------------------------------------------------------------------- 1 | function B = BernoulliFnc(num_cell, fullV) 2 | 3 | dV = zeros(1,num_cell+1); 4 | B = zeros(2, num_cell+1); 5 | 6 | %Bernoulli fnc. 7 | 8 | for i = 2:num_cell+1 %so dV(100) = dV(101: is at x=L) - dV(100, at x= l-dx). 9 | dV(i) = fullV(i)-fullV(i-1); %these fullV's ARE ACTUALLY PSI PRIME; ALREADY = V/Vt, when solved poisson. 10 | end 11 | 12 | for i = 2:num_cell+1 13 | B(1,i) = dV(i)/(exp(dV(i))-1.0); %B(+dV) 14 | %B(2,i) = -dV(i)/(exp(-dV(i))-1.0); %THIS IS EQUIVALENT TO OTHE 15 | %BELOW EXPERSSION 16 | B(2,i) = B(1,i)*exp(dV(i)); %B(-dV) 17 | end 18 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/src/Scharfetter-Gummel/SetAp_val.m: -------------------------------------------------------------------------------- 1 | function Ap_val = SetAp_val(num_elements, B) 2 | 3 | Ap = zeros(num_elements,3); 4 | 5 | %BE CAREFUL!: the setup of the sparse matrix elements is 6 | %non-trivial: last entry of Ap(:,1) 1st entry of 7 | %Ap(:,3) are unused b/c off diagonals have 1 less element than main 8 | %diagonal! 9 | 10 | for i=1:num_elements %I verified that ordering of columns is correct! 11 | Ap(i,2) = -(B(2,i+1) + B(1,i+1+1)); %I HAVE VERIFIED THAT ALL THE dV's properly match up with indices! All the extra +1's are b/c B starts at i=2 (1st pt inside device). 12 | end 13 | for i = 1:num_elements-1 14 | Ap(i,1) = B(1,i+1+1); %should be i+1+1 b/c have B(dVi)*p_i-1 (see fortran reindexed version) 15 | end 16 | Ap(num_elements, 1) = 0; %last element is unused 17 | for i = 2:num_elements 18 | Ap(i,3) = B(2,i+1); %1st element here corresponds to the 1st row of matrix: so need to use B3 --> corresponding to p(3) 19 | end 20 | Ap(1,3) = 0; %1st element of Ap(:,3) is unused 21 | 22 | 23 | Ap_val = spdiags(Ap,-1:1,num_elements,num_elements); -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Single-charge-carrier/src/Scharfetter-Gummel/Setbp.m: -------------------------------------------------------------------------------- 1 | function bp = Setbp(B, p_initial, num_cell, num_elements, Cp, U) 2 | 3 | bp = zeros(num_elements,1); 4 | 5 | %enforce boundary conditions through bp 6 | bp(1) = -B(1,2)*p_initial; %NOTE: scaled p_initial = 1 --> this is just like in fortran version 7 | bp(num_elements) = 0;%-B(1,nx-1)*p(nx); %ENFORCE RIGHT SIDE P IS = 0 8 | 9 | %introduce a net generation rate somewhere in the middle 10 | bp(ceil(num_cell/2.)) = -Cp*U; -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | *.smod 19 | 20 | # Compiled Static libraries 21 | *.lai 22 | *.la 23 | *.a 24 | *.lib 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | 31 | #data output 32 | *.txt 33 | 34 | #QT builds 35 | *.stash 36 | *.pdb 37 | *.Debug 38 | *.Release 39 | Makefile 40 | *.ilk 41 | *.user 42 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/1D_general_DD.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | CONFIG += console c++11 3 | CONFIG -= app_bundle 4 | CONFIG -= qt 5 | #QMAKE_CXXFLAGS_RELEASE += -Ox //Ox is "full optimization" for Msvc, seems no difference in speedfrom the default -O2 6 | 7 | SOURCES += \ 8 | photogeneration.cpp \ 9 | recombination.cpp \ 10 | thomas_tridiag_solve.cpp \ 11 | poisson.cpp \ 12 | continuity_n.cpp \ 13 | continuity_p.cpp \ 14 | parameters.cpp \ 15 | Utilities.cpp \ 16 | run_DD.cpp \ 17 | main.cpp \ 18 | optimization.cpp 19 | 20 | HEADERS += \ 21 | photogeneration.h \ 22 | recombination.h \ 23 | thomas_tridiag_solve.h \ 24 | poisson.h \ 25 | continuity_n.h \ 26 | continuity_p.h \ 27 | constants.h \ 28 | parameters.h \ 29 | Utilities.h \ 30 | run_DD.h \ 31 | optimization.h 32 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/README.md: -------------------------------------------------------------------------------- 1 | # Drift-Diffusion_models 2 | 3 | 1D Drift Diffusion 4 | 5 | This solves the semiconductor drift-diffusion equations in 1D for both electrons and holes using finite differences. The equations are Poisson eqn, 6 | continuity equation, and drift-diffusion equation which are solved in a decoupled iterative method (Gummel method). Scharfetter-Gummel 7 | discretization as well as linear mixing of old and new solutions is used to maintain stability. 8 | 9 | The code was originally developed for solar cells, but can be used for any semiconductor devices. 10 | 11 | ------------------------------------------------------------------------------------------------------ 12 | 13 | Input parameters are specified in parameters.inp. 14 | 15 | Other input files: 16 | Photogeneration rate: The generation rate file (filename can be specified in parameters.inp) should contain just 1 column of generation rates corresponding to each mesh point in the device (except the end points, x = 0 and x=L). An example generation rate file for device of 300nm thickness is included. 17 | 18 | Experimental JV curve (optional): File name can be specified in parameters.inp. File should contain 2 columns: 1st column are voltage values and 2nd are current values (in A/m^3). 19 | 20 | ------------------------------------------------------------------------------------------------------ 21 | 22 | Code can be compiled using the makefile or the QT Creator .pro project file (just open that and run within QT). 23 | 24 | ------------------------------------------------------------------------------------------------------ 25 | 26 | Version History: 27 | 28 | Current version 3.0 (under development): 29 | - Add optional automatic fitting algorithm to fit the model to an experimental JV curve. 30 | 31 | Past versions: 32 | v2.0 Object orientation. Parameter input from file. Speed improvements. 33 | v1.0 First working version. Parameters are in parameters.h. No object orientation. 34 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/Utilities.cpp: -------------------------------------------------------------------------------- 1 | #include "Utilities.h" 2 | #include "parameters.h" 3 | 4 | Utilities::Utilities() 5 | { 6 | } 7 | 8 | std::vector Utilities::linear_mix(const Parameters ¶ms, const std::vector &new_values,const std::vector &old_values) 9 | { 10 | static std::vector result(params.num_cell+1); //static so only allocate at 1st fnc call 11 | 12 | for (int i = 1; i < params.num_cell; i++) { //note: all the changing values in vectors start from 1 (0th index is a BC) 13 | result[i] = new_values[i]*params.w + old_values[i]*(1.0 - params.w); 14 | } 15 | 16 | return result; 17 | } 18 | 19 | 20 | void Utilities::write_details(const Parameters ¶ms, double Va, const std::vector &V, const std::vector &p, const std::vector &n, const std::vector &J_total, const std::vector &Un, const std::vector &PhotogenRate, const std::vector &R_Langevin) 21 | { 22 | static std::ofstream VaData; 23 | //Write charge densities, recombination rates, etc 24 | std::string filename = std::to_string(Va); 25 | filename += ".txt"; //add .txt extension 26 | VaData.open(filename); //this will need to have a string as file name 27 | for (int i = 1; i <= params.num_cell; i++) { 28 | VaData << std::setw(15) << std::setprecision(8) << params.dx*i; 29 | VaData << std::setw(15) << std::setprecision(8) << Vt*V[i]; 30 | VaData << std::setw(15) << std::setprecision(8) << params.N*p[i]; //setprecision(8) sets that use 8 sigfigs 31 | VaData << std::setw(15) << std::setprecision(8) << params.N*n[i]; 32 | VaData << std::setw(15) << std::setprecision(8) << J_total[i]; 33 | VaData << std::setw(15) << std::setprecision(8) << Un[i]; 34 | VaData << std::setw(15) << std::setprecision(8) << PhotogenRate[i]; 35 | VaData << std::setw(15) << std::setprecision(8) << R_Langevin[i]; 36 | VaData << std::setw(15) << std::setprecision(8) << params.w; 37 | VaData << std::setw(15) << std::setprecision(8) << params.tolerance < 5 | #include "parameters.h" 6 | #include 7 | #include 8 | #include 9 | 10 | class Utilities 11 | { 12 | public: 13 | Utilities(); 14 | 15 | //!Applies linear mixing (result = w*new_value + (1-w)*old_value) where w is the mixing factor. 16 | //! The mixing factor is in the \param params object. 17 | std::vector linear_mix(const Parameters ¶ms,const std::vector &new_values,const std::vector &old_values); 18 | 19 | //!This writes to output files the details of voltage \param V, carrier densities \param p and \param n, current \param J_total, net electron generation rate \param Un, photogeneration rate and Langevin recombination rate. 20 | //! The files are named according to the applied voltage \param Va of this data. 21 | void write_details(const Parameters ¶ms, double Va, const std::vector &V, const std::vector &p, const std::vector &n, const std::vector &J_total, const std::vector &Un, const std::vector &PhotogenRate, const std::vector &R_Langevin); 22 | 23 | //!Writes to a file the JV data. The file contains 3 columns, the applied voltage \param Va, the current \param J_total, 24 | //! and the number of iterations \param iter required to converge at that voltage. 25 | void write_JV(const Parameters ¶ms, std::ofstream &JV, double iter, double Va, const double J_value); 26 | 27 | }; 28 | 29 | #endif // UTILITIES_H 30 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/constants.h: -------------------------------------------------------------------------------- 1 | #ifndef CONSTANTS_H 2 | #define CONSTANTS_H 3 | 4 | #include 5 | 6 | //Physical Constants (these should not be changed) (constexpr b/c known at compile-time) 7 | constexpr double q = 1.60217646e-19; //elementary charge, C 8 | constexpr double kb = 1.3806503e-23; //Boltzmann const., J/k 9 | constexpr double T = 296.; //temperature K 10 | constexpr double Vt = (kb*T)/q; //thermal voltage V 11 | constexpr double epsilon_0 = 8.85418782e-12; //F/m 12 | 13 | 14 | #endif // CONSTANTS_H 15 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/continuity_n.h: -------------------------------------------------------------------------------- 1 | #ifndef CONTINUITY_N_H 2 | #define CONTINUITY_N_H 3 | 4 | #include 5 | #include "parameters.h" //needs this to know what parameters are 6 | #include "constants.h" 7 | 8 | class Continuity_n 9 | { 10 | public: 11 | Continuity_n(const Parameters ¶ms); 12 | 13 | //!Sets up the matrix equation An*n = bn for continuity equation for electrons. 14 | //!\param V stores the voltage and is needed to calculate Bernoulli fnc.'s. 15 | //!\param Un stores the net generation rate, needed for the right hand side. 16 | void setup_eqn(const std::vector &V, const std::vector &Un); 17 | 18 | //getters (const keyword ensures that fnc doesn't change anything) 19 | std::vector get_main_diag() const {return main_diag;} 20 | std::vector get_upper_diag() const {return upper_diag;} 21 | std::vector get_lower_diag() const {return lower_diag;} 22 | std::vector get_rhs() const {return rhs;} 23 | std::vector get_n_mob() const {return n_mob;} 24 | std::vector get_B_n1() const {return B_n1;} 25 | std::vector get_B_n2() const {return B_n2;} 26 | double get_n_leftBC() const {return n_leftBC;} 27 | double get_n_rightBC() const {return n_rightBC;} 28 | 29 | private: 30 | std::vector main_diag; 31 | std::vector upper_diag; 32 | std::vector lower_diag; 33 | std::vector rhs; 34 | std::vector n_mob; 35 | std::vector B_n1; //bernoulli (+dV) 36 | std::vector B_n2; //bernoulli (-dV) 37 | double Cn; 38 | double n_leftBC; //this is anode 39 | double n_rightBC; 40 | 41 | //!Calculates the Bernoulli functions and updates member arrays 42 | //! \param B_n1 = B(+dV) and \param B_n2 = (-dV) 43 | void BernoulliFnc_n(const std::vector &V); 44 | 45 | void set_main_diag(); 46 | void set_upper_diag(); 47 | void set_lower_diag(); 48 | void set_rhs(const std::vector &Un); 49 | }; 50 | 51 | #endif // CONTINUITY_N_H 52 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/continuity_p.h: -------------------------------------------------------------------------------- 1 | #ifndef CONTINUITY_P_H 2 | #define CONTINUITY_P_H 3 | 4 | #include 5 | #include "parameters.h" //needs this to know what parameters is 6 | #include "constants.h" 7 | 8 | class Continuity_p 9 | { 10 | public: 11 | Continuity_p(const Parameters ¶ms); 12 | 13 | //!Sets up the matrix equation Ap*p = bp for continuity equation for holes. 14 | //!\param V stores the voltage and is needed to calculate Bernoulli fnc.'s. 15 | //!\param Up stores the net generation rate, needed for the right hand side. 16 | void setup_eqn(const std::vector &V, const std::vector & Up); 17 | 18 | //getters 19 | std::vector get_main_diag() const {return main_diag;} 20 | std::vector get_upper_diag() const {return upper_diag;} 21 | std::vector get_lower_diag() const {return lower_diag;} 22 | std::vector get_rhs() const {return rhs;} 23 | std::vector get_p_mob() const {return p_mob;} 24 | std::vector get_B_p1() const {return B_p1;} 25 | std::vector get_B_p2() const {return B_p2;} 26 | double get_p_leftBC() const {return p_leftBC;} 27 | double get_p_rightBC() const {return p_rightBC;} 28 | 29 | private: 30 | std::vector main_diag; 31 | std::vector upper_diag; 32 | std::vector lower_diag; 33 | std::vector rhs; 34 | std::vector p_mob; 35 | std::vector B_p1; //bernoulli (+dV) 36 | std::vector B_p2; //bernoulli (-dV) 37 | double Cp; 38 | double p_leftBC; 39 | double p_rightBC; 40 | 41 | //!Calculates the Bernoulli functions and updates member arrays 42 | //! \param B_n1 = B(+dV) and \param B_n2 = (-dV) 43 | void BernoulliFnc_p(const std::vector &V); 44 | 45 | void set_main_diag(); 46 | void set_upper_diag(); 47 | void set_lower_diag(); 48 | void set_rhs(const std::vector &Up); 49 | }; 50 | 51 | #endif // CONTINUITY_P_H 52 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/make: -------------------------------------------------------------------------------- 1 | # Comment lines 2 | # Here we define compiler option, libraries and the target 3 | FLAGS = -O2 -Wall 4 | 5 | # Here we make the executable file 6 | SRCS = main.cpp bernoulli.cpp continuity_n.cpp continuity_p.cpp parameters.cpp photogeneration.cpp poisson.cpp recombination.cpp thomas_tridiag_solve.cpp 7 | OBJS = $(subst .cpp,.o,$(SRCS)) 8 | all: 1D_DD 9 | 10 | # Whereas here we create the object file 11 | DD: $(OBJS) constants.h 12 | g++ ${FLAGS} -o 1D_DD $(OBJS) 13 | 14 | %.o: %.cpp constants.h 15 | g++ $(FLAGS) -c $< 16 | 17 | # Clean 18 | clean: 19 | rm *.o ./1D_DD 20 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/parameters.h: -------------------------------------------------------------------------------- 1 | #ifndef PARAMETERS_H 2 | #define PARAMETERS_H 3 | 4 | #include "constants.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | struct Parameters //parameters need to be accessble, so all members are public. 14 | { 15 | Parameters(){} 16 | void reduce_w(){w = w/w_reduce_factor;} 17 | void relax_tolerance(){tolerance = tolerance*tol_relax_factor;} 18 | void use_tolerance_eq() {tolerance = tolerance_eq;} 19 | void use_tolerance_i() {tolerance = tolerance_i;} 20 | void use_w_i() {w = w_i;} 21 | void use_w_eq() {w = w_eq;} 22 | 23 | void Initialize(); 24 | void isPositive(double input, const std::string &comment); 25 | void isPositive(int input, const std::string &comment); 26 | void isNegative(double input, const std::string &comment); 27 | void isNegative(int input, const std::string &comment); 28 | 29 | double N_LUMO, N_HOMO, phi_a, phi_c, eps_active, p_mob_active, n_mob_active; 30 | double dx, mobil; 31 | double E_gap, active_CB, active_VB, WF_anode, WF_cathode, N, Nsqrd; 32 | double Photogen_scaling, k_rec; 33 | 34 | double w; 35 | double w_reduce_factor; 36 | double tolerance, tolerance_eq; 37 | double tol_relax_factor; 38 | double Vmin, Vmax; 39 | 40 | double tolerance_i, w_i, w_eq; 41 | double L; 42 | int num_cell; 43 | std::string GenRateFileName; 44 | double Va_min, Va_max, increment; 45 | 46 | //optimization (auto fitting) parameters 47 | bool auto_fit; 48 | int optim_method; 49 | double fit_tolerance; 50 | double optim_max_iter; 51 | std::string exp_data_file_name; 52 | double Photogen_scaling_min, Photogen_scaling_max; 53 | double n_mob_active_max, n_mob_active_min; 54 | double p_mob_active_max, p_mob_active_min; 55 | double k_rec_max, k_rec_min; 56 | 57 | bool PSO_Clerc_Kennedy; 58 | 59 | std::vector vars; //will store pointers to the parameters/variables which are optimizing 60 | std::vector vars_min; //stores the min of the ranges 61 | std::vector vars_max; 62 | }; 63 | 64 | #endif // PARAMETERS_H 65 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/parameters.inp: -------------------------------------------------------------------------------- 1 | //NOTE:IF-HAVE-ANY-SPACES-IN-COMMENTS-IT-WILL-FAIL 2 | 300.0e-9 //device-thickness(m) 3 | 300 //num_cell 4 | 1e24 //N-LUMO 5 | 1e24 //N-HOMO 6 | 7.0e+27 //Photogeneration-scaling 7 | 0.2 //phi_a 8 | 0.1 //phi_c 9 | 3.0 //eps_active 10 | 4.5e-06 //p_mob_active 11 | 4.5e-06 //n_mob_active 12 | 5e-6 //mobil 13 | 1.5 //E_gap 14 | -3.9 //active_CB 15 | -5.4 //active_VB 16 | 4.8 //WF_anode 17 | 3.7 //WF_cathode 18 | 6e-17 //k_rec 19 | 1.0e-9 //dx 20 | 21 | 22 | -0.5 //Va_min 23 | 1.05 //Va_max 24 | 0.01 //increment 25 | 0.01 //w_eq 26 | 0.2 //w_i 27 | 5e-12 //tolerance_i 28 | 2.0 //w_reduce_factor 29 | 10.0 //tol_relax_factor 30 | gen_rate.inp //GenRateFileName 31 | 32 | //optimization(auto-fit)_parameters 33 | 1 //auto-fit?(1==true,0=false) 34 | 1 //optim_method:1==gradient-descent,2==particle-swarm 35 | 50 //max_iter_for-optimization_loop-(only-needed-if-auto-fit==1) 36 | 500 //fit-tolerance-(only-needed-if-auto-fit==1)-NOTE-this-is-for-sum-of-squares-so-relativelyLARGE 37 | experiment_JV.inp //filename_to_fit_to-(only-needed-if-auto-fit==1) 38 | 39 | //optimization-parameter-ranges 40 | 1e27 //Photogeneration-scaling-MIN 41 | 3e28 //Photogeneration-scaling-MAX 42 | 1e-7 //n-Mobility-MIN 43 | 1e-5 //n-Mobility-MAX 44 | 1e-7 //p-Mobility-MIN 45 | 1e-5 //p-Mobility-MAX 46 | 6e-18 //k_rec-MIN 47 | 6e-16 //k_rec-MAX 48 | 49 | 50 | //Particle-swarm-_parameters 51 | 0 //use-clerc-kennedy-constriction(1==true)-SEEMS-SLOWER-CONVERGE-IF-USE 52 | 53 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/photogeneration.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "photogeneration.h" 5 | 6 | //constructor definition 7 | Photogeneration::Photogeneration(const Parameters ¶ms, double photogen_scaling, const std::string gen_rate_file_name){ 8 | 9 | PhotogenRate.resize(params.num_cell); 10 | PhotogenRate_max = photogen_scaling; 11 | 12 | std::ifstream GenRateFile; 13 | 14 | GenRateFile.open(gen_rate_file_name); 15 | //check if file was opened 16 | if (!GenRateFile) { 17 | std::cerr << "Unable to open file gen_rate.txt"; 18 | exit(1); // call system to stop 19 | } 20 | 21 | for (int i = 1; i <= params.num_cell-1; i++) { 22 | GenRateFile >> PhotogenRate[i]; 23 | //std::cout << "G(i) " << G[i] < 5 | #include "constants.h" 6 | #include "parameters.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | class Photogeneration 13 | { 14 | public: 15 | //!Constructor will get the generation rate from file. 16 | //!Generation rate file should contain num_cell -2 number of entries in a single column, corresponding to 17 | //!the the generation rate at each mesh point (except the endpoints). 18 | //! \param photogen_scaling is the scaling factor obtained from fit to get the correct short-circuit current. 19 | Photogeneration(const Parameters ¶ms, double photogen_scaling, const std::string gen_rate_file_name); 20 | 21 | std::vector getPhotogenRate() {return PhotogenRate;} 22 | private: 23 | std::vector PhotogenRate; 24 | double PhotogenRate_max; 25 | }; 26 | 27 | 28 | 29 | #endif // PHOTOGENERATION_H 30 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/poisson.cpp: -------------------------------------------------------------------------------- 1 | #include "poisson.h" 2 | #include 3 | 4 | Poisson::Poisson(const Parameters ¶ms) 5 | { 6 | main_diag.resize(params.num_cell); 7 | upper_diag.resize(params.num_cell-1); 8 | lower_diag.resize(params.num_cell-1); 9 | rhs.resize(params.num_cell); 10 | epsilon.resize(params.num_cell +1); 11 | 12 | CV = params.N*params.dx*params.dx*q/(epsilon_0*Vt); 13 | std::fill(epsilon.begin(), epsilon.end(), params.eps_active); 14 | } //constructor) 15 | 16 | 17 | void Poisson::setup_matrix() //Note: this is on purpose different than the setup_eqn used for Continuity eqn's, b/c I need to setup matrix only once 18 | { 19 | set_main_diag(); 20 | set_lower_diag(); 21 | set_upper_diag(); 22 | } 23 | 24 | 25 | //---------------Setup AV diagonals (Poisson solve)--------------------------------------------------------------- 26 | //this is a in tridiag_solver 27 | void Poisson::set_main_diag() 28 | { 29 | for (int i = 1; i < main_diag.size(); i++) { 30 | main_diag[i] = -2.*epsilon[i]; 31 | } 32 | } 33 | 34 | //this is b in tridiag_solver 35 | void Poisson::set_upper_diag() 36 | { 37 | for (int i = 1; i < upper_diag.size(); i++) { 38 | upper_diag[i] = epsilon[i]; 39 | } 40 | } 41 | 42 | //this is c in tridiag_solver 43 | void Poisson::set_lower_diag() 44 | { 45 | for (int i = 1; i < lower_diag.size(); i++) { 46 | lower_diag[i] = epsilon[i]; 47 | } 48 | } 49 | 50 | //------------------------------------------------------------------------------------ 51 | void Poisson::set_rhs(const std::vector &n, const std::vector &p, double V_leftBC, double V_rightBC) 52 | { 53 | for (int i = 1; i <= rhs.size()-1; i++) { 54 | rhs[i] = CV*(n[i] - p[i]); 55 | //std::cout << "bV " << bV[i] << std::endl; 56 | } 57 | rhs[1] -= epsilon[0]*V_leftBC; 58 | rhs[rhs.size()-1] -= epsilon[rhs.size()]*V_rightBC; 59 | } 60 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/poisson.h: -------------------------------------------------------------------------------- 1 | #ifndef POISSON_H 2 | #define POISSON_H 3 | 4 | #include 5 | #include "parameters.h" //needs this to know what paramsation is 6 | #include "constants.h" 7 | 8 | class Poisson 9 | { 10 | public: 11 | Poisson(const Parameters ¶ms); 12 | 13 | //!Setup Poisson equation matrix AV. Note that the matrix will usually be the same throughout the simulation 14 | //! since it only depends on dielectric constant, so this function should be called only once. 15 | void setup_matrix(); 16 | 17 | //!Setup the right hand side of Poisson equation. This depends on the electron density \param n, 18 | //! hole density \param p, and left and right boundary conditions \param V_leftBC and \param V_rightBC 19 | void set_rhs(const std::vector &n, const std::vector &p, double V_leftBC, double V_rightBC); 20 | 21 | //getters 22 | std::vector get_main_diag() const {return main_diag;} 23 | std::vector get_upper_diag() const {return upper_diag;} 24 | std::vector get_lower_diag() const {return lower_diag;} 25 | std::vector get_rhs() const {return rhs;} 26 | 27 | private: 28 | std::vector main_diag; 29 | std::vector upper_diag; 30 | std::vector lower_diag; 31 | std::vector rhs; 32 | std::vector epsilon; 33 | double CV; //relative permitivity was moved into the matrix 34 | 35 | void set_main_diag(); 36 | void set_upper_diag(); 37 | void set_lower_diag(); 38 | }; 39 | 40 | #endif // POISSON_H 41 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/recombination.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "recombination.h" 4 | 5 | Recombo:: Recombo(const Parameters ¶ms) //constructor 6 | { 7 | k_rec = params.k_rec; 8 | R_Langevin.resize(params.num_cell); 9 | E_trap = params.active_VB + params.E_gap/2.0; //trap assisted recombo is most effective when trap is located mid-gap--> take VB and add 1/2 of bandgap 10 | n1 = params.N_LUMO*exp(-(params.active_CB - E_trap)/Vt); 11 | p1 = params.N_HOMO*exp(-(E_trap - params.active_VB)/Vt); 12 | } 13 | 14 | std::vector Recombo::ComputeR_Langevin(const Parameters ¶ms, const std::vector &n, const std::vector &p) 15 | { 16 | for (int i = 1; i < p.size(); i++) { 17 | R_Langevin[i] = k_rec*(params.N*params.N*n[i]*p[i] - n1*p1); 18 | if (R_Langevin[i] < 0.0) R_Langevin[i] = 0.0; //negative recombo is unphysical 19 | } 20 | 21 | return R_Langevin; 22 | } 23 | 24 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/recombination.h: -------------------------------------------------------------------------------- 1 | #ifndef RECOMBINATION_H 2 | #define RECOMBINATION_H 3 | 4 | #include 5 | #include "constants.h" 6 | #include "parameters.h" 7 | 8 | class Recombo{ 9 | public: 10 | //!Constructor sets up the recombination member parameters using the input from \param params 11 | Recombo(const Parameters ¶ms); 12 | 13 | //!Computes bimolecular Langevin recombination rate. 14 | //! This depends on the electron \param n and hole \param p density. 15 | std::vector ComputeR_Langevin(const Parameters ¶ms, const std::vector &n, const std::vector &p); 16 | 17 | private: 18 | double k_rec; //!bimolecular recombination coefficient 19 | double E_trap; //!Trap level energy 20 | double n1; 21 | double p1; 22 | std::vector R_Langevin; //!store the Langevin recombination rate 23 | }; 24 | 25 | 26 | #endif // RECOMBINATION_H 27 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/run_dd.h: -------------------------------------------------------------------------------- 1 | #ifndef RUN_DD_H 2 | #define RUN_DD_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include //allows to use fill and min 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "constants.h" //these contain physics constants only 14 | #include "parameters.h" 15 | #include "poisson.h" 16 | #include "continuity_p.h" 17 | #include "continuity_n.h" 18 | #include "recombination.h" 19 | #include "photogeneration.h" 20 | #include "thomas_tridiag_solve.h" 21 | #include "Utilities.h" 22 | 23 | std::vector run_DD(Parameters ¶ms); 24 | 25 | 26 | 27 | 28 | #endif // RUN_DD_H 29 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/thomas_tridiag_solve.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | //!This function uses Thomas algorithm for tridiagonal matrix (special case of Gaussian elimination) 5 | //! diagonal = array containing elements of main diagonal. indices: (a1.....an) 6 | //! b = array containing elements of upper diagonal. indices (b1....b_n-1) 7 | //!c = array containing elements of lower diagonal. indices (c1...c_n-1) 8 | 9 | //!\author Timofey Golubev 10 | 11 | std::vector Thomas_solve(const std::vector &a,const std::vector &b,const std::vector &c, std::vector &rhs){ 12 | 13 | int num_elements = a.size()-1; //matrices indexed from 0, but we use it from 1 here 14 | double cdiag_ratio; 15 | std::vector x(num_elements+2); 16 | std::vector diagonal = a; //this is so the actual a value is unchanged, so can precalculate a before the loop... 17 | 18 | //Forward substitution 19 | for (int i = 2; i <= num_elements; i++) { 20 | cdiag_ratio = c[i-1]/diagonal[i-1]; //i-1 b/c need to use c1 and a1, and i starts at 2... 21 | diagonal[i] -= cdiag_ratio*b[i-1]; 22 | rhs[i] -= cdiag_ratio*rhs[i-1]; 23 | } 24 | 25 | //Backward substitution 26 | x[num_elements] = rhs[num_elements]/diagonal[num_elements]; //linear eqn corresponding to last row 27 | for (int i = num_elements; i > 1; i--) 28 | x[i-1] = (rhs[i-1] - x[i]*b[i-1])/diagonal[i-1]; 29 | 30 | return x; 31 | } 32 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/thomas_tridiag_solve.h: -------------------------------------------------------------------------------- 1 | #ifndef THOMAS_TRIDIAG_SOLVE_H 2 | #define THOMAS_TRIDIAG_SOLVE_H 3 | 4 | #include 5 | 6 | //!This function uses Thomas algorithm for tridiagonal matrix (special case of Gaussian elimination) 7 | //! diagonal = array containing elements of main diagonal. indices: (a1.....an) 8 | //! b = array containing elements of upper diagonal. indices (b1....b_n-1) 9 | //!c = array containing elements of lower diagonal. indices (c1...c_n-1) 10 | std::vector Thomas_solve(const std::vector &a, const std::vector &b,const std::vector &c, std::vector &rhs); 11 | 12 | 13 | 14 | #endif // THOMAS_TRIDIAG_SOLVE_H 15 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v1.0/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | *.smod 19 | 20 | # Compiled Static libraries 21 | *.lai 22 | *.la 23 | *.a 24 | *.lib 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | 31 | #data output 32 | *.txt 33 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v1.0/1D_general_DD.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | CONFIG += console c++11 3 | CONFIG -= app_bundle 4 | CONFIG -= qt 5 | 6 | SOURCES += \ 7 | bernoulli.cpp \ 8 | photogeneration.cpp \ 9 | recombination.cpp \ 10 | Set_diagonals.cpp \ 11 | set_rhs.cpp \ 12 | thomas_tridiag_solve.cpp \ 13 | main.cpp 14 | 15 | HEADERS += \ 16 | bernoulli.h \ 17 | parameters.h \ 18 | photogeneration.h \ 19 | recombination.h \ 20 | Set_diagonals.h \ 21 | set_rhs.h \ 22 | thomas_tridiag_solve.h 23 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v1.0/README.md: -------------------------------------------------------------------------------- 1 | # Drift-Diffusion_models 2 | 3 | This solves the semiconductor drift-diffusion equations in 1D for both electrons and holes using finite differences. The equations are Poisson eqn, 4 | continuity equation, and drift-diffusion equation which are solved in a decoupled iterative method (Gummel method). Scharfetter-Gummel 5 | discretization as well as linear mixing of old and new solutions is used to maintain stability. 6 | 7 | The gen_rate.txt input file is needed for photogeneration.cpp, or can comment that section out and use an analytic expression or set it to zero if are not 8 | studying a device under illumination. The gen_rate.txt file should contain just 1 column of generation rates corresponding to each mesh point in the device (except the end points, x = 0 and x=L). An example generation rate file for device of 300nm thickness is included. 9 | 10 | The code was originally developed for solar cells, but can be used for any semiconductor devices. 11 | 12 | Code can be compiled using the makefile or the QT Creator .pro project file (just open that and run within QT). 13 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v1.0/Set_diagonals.h: -------------------------------------------------------------------------------- 1 | #ifndef SET_AV_DIAGS_H 2 | #define SET_AV_DIAGS_H 3 | 4 | #include 5 | #include "parameters.h" 6 | 7 | void set_main_AVdiag(const std::vector &epsilon, std::vector &a); 8 | void set_upper_AVdiag(const std::vector &epsilon, std::vector &b); 9 | void set_lower_AVdiag(const std::vector &epsilon, std::vector &c); 10 | void set_main_An_diag(const std::vector &n_mob, const std::vector &B_n1, const std::vector &B_n2, std::vector &main_diag); 11 | void set_upper_An_diag(const std::vector &n_mob, const std::vector &B_n1, std::vector &upper_diag); 12 | void set_lower_An_diag(const std::vector &n_mob, const std::vector &B_n2, std::vector &lower_diag); 13 | void set_main_Ap_diag(const std::vector &p_mob, const std::vector &B_p1, const std::vector &B_p2, std::vector &main_diag); 14 | void set_upper_Ap_diag(const std::vector &p_mob, const std::vector &B_p2, std::vector &upper_diag); 15 | void set_lower_Ap_diag(const std::vector &p_mob, const std::vector &B_p1, std::vector &lower_diag); 16 | 17 | #endif // SET_AV_DIAGS_H 18 | 19 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v1.0/bernoulli.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include"parameters.h" 4 | #include 5 | 6 | void BernoulliFnc_n(const std::vector &V, std::vector &B_n1, std::vector &B_n2){ //NOTE: this V has the right side bndry condition appended 7 | std::vector dV(num_cell+1); 8 | 9 | for(int i = 1; i<=num_cell;i++){ 10 | dV[i] = V[i]-V[i-1]; 11 | } 12 | 13 | for(int i = 1; i<=num_cell;i++){ 14 | B_n1[i] = dV[i]/(exp(dV[i])-1.0); 15 | B_n2[i] = B_n1[i]*exp(dV[i]); 16 | } 17 | } 18 | 19 | 20 | void BernoulliFnc_p(const std::vector &V, std::vector &B_p1, std::vector &B_p2){ //NOTE: this V has the right side bndry condition appended 21 | std::vector dV(num_cell+1); 22 | 23 | for(int i = 1; i<=num_cell;i++){ 24 | dV[i] = V[i]-V[i-1]; 25 | } 26 | 27 | for(int i = 1; i<=num_cell;i++){ 28 | B_p1[i] = dV[i]/(exp(dV[i])-1.0); 29 | B_p2[i] = B_p1[i]*exp(dV[i]); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v1.0/bernoulli.h: -------------------------------------------------------------------------------- 1 | #ifndef BERNOULLI_H 2 | #define BERNOULLI_H 3 | 4 | 5 | #include 6 | #include "parameters.h" 7 | 8 | void BernoulliFnc_n(const std::vector &V, std::vector &B_n1, std::vector &B_n2); 9 | void BernoulliFnc_p(const std::vector &V, std::vector &B_p1, std::vector &B_p2); 10 | 11 | #endif // BERNOULLI_H 12 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v1.0/make: -------------------------------------------------------------------------------- 1 | # Comment lines 2 | # Here we define compiler option, libraries and the target 3 | FLAGS = -O2 -Wall 4 | 5 | # Here we make the executable file 6 | SRCS = main.cpp bernoulli.cpp photogeneration.cpp recombination.cpp Set_diagonals.cpp set_rhs.cpp thomas_tridiag_solve.cpp 7 | OBJS = $(subst .cpp,.o,$(SRCS)) 8 | all: DD 9 | 10 | # Whereas here we create the object file 11 | DD: $(OBJS) parameters.h #need to put this explicitely b/ doesn't have an associated .cpp 12 | g++ ${FLAGS} -o DD $(OBJS) 13 | # export OMP_NUM_THREADS=20 14 | # ./md 15 | 16 | %.o: %.cpp parameters.h 17 | g++ $(FLAGS) -c $< 18 | 19 | 20 | # Clean 21 | clean: 22 | rm *.o ./DD 23 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v1.0/photogeneration.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "parameters.h" 5 | #include 6 | #include 7 | 8 | //Generation rate file should contain num_cell -2 number of entries in a single column, corresponding to 9 | //the the generation rate at each mesh point (except the endpoints). 10 | std::vector PhotogenerationRate(){ 11 | 12 | std::vector G(num_cell); 13 | 14 | std::ifstream GenRateFile; 15 | 16 | GenRateFile.open("gen_rate.txt"); 17 | //check if file was opened 18 | if (!GenRateFile) { 19 | std::cerr << "Unable to open file gen_rate.txt"; 20 | exit(1); // call system to stop 21 | } 22 | for(int i=1;i<=num_cell-1;i++){ 23 | GenRateFile >> G[i]; 24 | //std::cout << "G(i) " << G[i] < 5 | #include "parameters.h" 6 | 7 | std::vector PhotogenerationRate(); 8 | 9 | #endif // PHOTOGENERATION_H 10 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v1.0/recombination.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "parameters.h" 3 | #include 4 | 5 | void ComputeR_Langevin(const std::vector &n, const std::vector &p, std::vector &R_Langevin){ 6 | for(int i = 1;i<= num_cell-1;i++){ 7 | R_Langevin[i] = k_rec*(Nsqrd*n[i]*p[i] - n1*p1); 8 | if(R_Langevin[i] < 0.0) R_Langevin[i] = 0.0; //negative recombo is unphysical 9 | } 10 | } 11 | 12 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v1.0/recombination.h: -------------------------------------------------------------------------------- 1 | #ifndef RECOMBINATION_H 2 | #define RECOMBINATION_H 3 | 4 | #include 5 | #include "parameters.h" 6 | 7 | void ComputeR_Langevin(const std::vector &n, const std::vector &p, std::vector &R_Langevin); 8 | 9 | 10 | #endif // RECOMBINATION_H 11 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v1.0/set_rhs.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include"parameters.h" 3 | #include 4 | 5 | void set_bp(const std::vector &p_mob,const std::vector &B_p1, const std::vector &B_p2, double p_leftBC, double p_rightBC, std::vector &Up, std::vector &bp){ 6 | for(int i = 1; i<=num_cell-1; i++){ 7 | bp[i] = -Cp*Up[i]; 8 | } 9 | //BCs 10 | bp[1] -= p_mob[0]*B_p1[1]*p_leftBC; 11 | bp[num_cell-1] -= p_mob[num_cell]*B_p2[num_cell]*p_rightBC; 12 | } 13 | 14 | void set_bn(const std::vector &n_mob, const std::vector &B_n1, const std::vector &B_n2, double n_leftBC, double n_rightBC, std::vector &Un, std::vector &bn){ 15 | for(int i = 1; i<=num_cell-1; i++){ 16 | bn[i] = -Cn*Un[i]; 17 | } 18 | //BCs 19 | bn[1] -= n_mob[0]*B_n2[1]*n_leftBC; 20 | bn[num_cell-1] -= n_mob[num_cell]*B_n1[num_cell]*n_rightBC; 21 | } 22 | 23 | void set_bV(const std::vector &epsilon, const std::vector &n, const std::vector &p, double V_leftBC, double V_rightBC, std::vector &bV){ 24 | for(int i = 1;i<=num_cell-1; i++){ 25 | bV[i] = CV*(n[i] - p[i]); 26 | //std::cout << "bV " << bV[i] << std::endl; 27 | } 28 | bV[1] -= epsilon[0]*V_leftBC; 29 | bV[num_cell-1] -= epsilon[num_cell]*V_rightBC; 30 | } 31 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v1.0/set_rhs.h: -------------------------------------------------------------------------------- 1 | #ifndef SET_RHS_H 2 | #define SET_RHS_H 3 | 4 | #include 5 | #include "parameters.h" 6 | 7 | void set_bp(const std::vector &p_mob,const std::vector &B_p1, const std::vector &B_p2, double p_leftBC, double p_rightBC, std::vector &Up, std::vector &bp); 8 | void set_bn(const std::vector &n_mob,const std::vector &B_n1, const std::vector &B_n2, double n_leftBC, double n_rightBC, std::vector &Un, std::vector &bn); 9 | void set_bV(const std::vector &epsilon,const std::vector &n, const std::vector &p, double V_leftBC, double V_rightBC, std::vector &bV); 10 | 11 | #endif // SET_RHS_H 12 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v1.0/thomas_tridiag_solve.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | //This function uses Thomas algorithm for tridiagonal matrix (special case of Gaussian elimination) 5 | // diagonal = array containing elements of main diagonal. indices: (a1.....an) 6 | // b = array containing elements of upper diagonal. indices (b1....b_n-1) 7 | //c = array containing elements of lower diagonal. indices (c1...c_n-1) 8 | 9 | //Timofey Golubev 10 | 11 | std::vector Thomas_solve(const std::vector &a,const std::vector &b,const std::vector &c, std::vector &rhs){ 12 | 13 | int num_elements = a.size()-1; //matrices indexed from 0, but we use it from 1 here 14 | double cdiag_ratio; 15 | std::vector x(num_elements+2); 16 | std::vector diagonal = a; //this is so the actual a value is unchanged, so can precalculate a before the loop... 17 | 18 | //I checked and there is no oppurtunity for more precalculation here 19 | 20 | //Forward substition 21 | for (int i = 2; i <= num_elements; i++) { 22 | cdiag_ratio = c[i-1]/diagonal[i-1]; //i-1 b/c need to use c1 and a1, and i starts at 2... 23 | diagonal[i] -= cdiag_ratio*b[i-1]; 24 | rhs[i] -= cdiag_ratio*rhs[i-1]; 25 | } 26 | 27 | //Backward substitution 28 | x[num_elements] = rhs[num_elements]/diagonal[num_elements]; //linear eqn corresponding to last row 29 | for (int i = num_elements; i >1; i--) x[i-1] = (rhs[i-1]-x[i]*b[i-1])/diagonal[i-1]; 30 | 31 | return x; 32 | } 33 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v1.0/thomas_tridiag_solve.h: -------------------------------------------------------------------------------- 1 | #ifndef THOMAS_TRIDIAG_SOLVE_H 2 | #define THOMAS_TRIDIAG_SOLVE_H 3 | 4 | #include 5 | 6 | std::vector Thomas_solve(const std::vector &a, const std::vector &b,const std::vector &c, std::vector &rhs); 7 | 8 | 9 | 10 | #endif // THOMAS_TRIDIAG_SOLVE_H 11 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v2.0/1D_general_DD_v2.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | CONFIG += console c++11 3 | CONFIG -= app_bundle 4 | CONFIG -= qt 5 | #QMAKE_CXXFLAGS_RELEASE += -Ox //Ox is "full optimization" for Msvc, seems no difference in speedfrom the default -O2 6 | 7 | SOURCES += \ 8 | main.cpp \ 9 | photogeneration.cpp \ 10 | recombination.cpp \ 11 | thomas_tridiag_solve.cpp \ 12 | poisson.cpp \ 13 | continuity_n.cpp \ 14 | continuity_p.cpp \ 15 | parameters.cpp \ 16 | Utilities.cpp 17 | 18 | 19 | HEADERS += \ 20 | photogeneration.h \ 21 | recombination.h \ 22 | thomas_tridiag_solve.h \ 23 | poisson.h \ 24 | continuity_n.h \ 25 | continuity_p.h \ 26 | constants.h \ 27 | parameters.h \ 28 | Utilities.h 29 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v2.0/Utilities.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILITIES_H 2 | #define UTILITIES_H 3 | 4 | #include 5 | #include "parameters.h" 6 | #include 7 | #include 8 | #include 9 | 10 | class Utilities 11 | { 12 | public: 13 | Utilities(); 14 | 15 | //!Applies linear mixing (result = w*new_value + (1-w)*old_value) where w is the mixing factor. 16 | //! The mixing factor is in the \param params object. 17 | std::vector linear_mix(const Parameters ¶ms,const std::vector &new_values,const std::vector &old_values); 18 | 19 | //!This writes to output files the details of voltage \param V, carrier densities \param p and \param n, current \param J_total, net electron generation rate \param Un, photogeneration rate and Langevin recombination rate. 20 | //! The files are named according to the applied voltage \param Va of this data. 21 | void write_details(const Parameters ¶ms, double Va, const std::vector &V, const std::vector &p, const std::vector &n, const std::vector &J_total, const std::vector &Un, const std::vector &PhotogenRate, const std::vector &R_Langevin); 22 | 23 | //!Writes to a file the JV data. The file contains 3 columns, the applied voltage \param Va, the current \param J_total, 24 | //! and the number of iterations \param iter required to converge at that voltage. 25 | void write_JV(const Parameters ¶ms, std::ofstream &JV, double iter, double Va, const std::vector &J_total); 26 | 27 | }; 28 | 29 | #endif // UTILITIES_H 30 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v2.0/constants.h: -------------------------------------------------------------------------------- 1 | #ifndef CONSTANTS_H 2 | #define CONSTANTS_H 3 | 4 | #include 5 | 6 | //Physical Constants (these should not be changed) (constexpr b/c known at compile-time) 7 | constexpr double q = 1.60217646e-19; //elementary charge, C 8 | constexpr double kb = 1.3806503e-23; //Boltzmann const., J/k 9 | constexpr double T = 296.; //temperature K 10 | constexpr double Vt = (kb*T)/q; //thermal voltage V 11 | constexpr double epsilon_0 = 8.85418782e-12; //F/m 12 | 13 | 14 | #endif // CONSTANTS_H 15 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v2.0/continuity_n.h: -------------------------------------------------------------------------------- 1 | #ifndef CONTINUITY_N_H 2 | #define CONTINUITY_N_H 3 | 4 | #include 5 | #include "parameters.h" //needs this to know what parameters are 6 | #include "constants.h" 7 | 8 | class Continuity_n 9 | { 10 | public: 11 | Continuity_n(const Parameters ¶ms); 12 | 13 | //!Sets up the matrix equation An*n = bn for continuity equation for electrons. 14 | //!\param V stores the voltage and is needed to calculate Bernoulli fnc.'s. 15 | //!\param Un stores the net generation rate, needed for the right hand side. 16 | void setup_eqn(const std::vector &V, const std::vector &Un); 17 | 18 | //getters (const keyword ensures that fnc doesn't change anything) 19 | std::vector get_main_diag() const {return main_diag;} 20 | std::vector get_upper_diag() const {return upper_diag;} 21 | std::vector get_lower_diag() const {return lower_diag;} 22 | std::vector get_rhs() const {return rhs;} 23 | std::vector get_n_mob() const {return n_mob;} 24 | std::vector get_B_n1() const {return B_n1;} 25 | std::vector get_B_n2() const {return B_n2;} 26 | double get_n_leftBC() const {return n_leftBC;} 27 | double get_n_rightBC() const {return n_rightBC;} 28 | 29 | private: 30 | std::vector main_diag; 31 | std::vector upper_diag; 32 | std::vector lower_diag; 33 | std::vector rhs; 34 | std::vector n_mob; 35 | std::vector B_n1; //bernoulli (+dV) 36 | std::vector B_n2; //bernoulli (-dV) 37 | double Cn; 38 | double n_leftBC; //this is anode 39 | double n_rightBC; 40 | 41 | //!Calculates the Bernoulli functions and updates member arrays 42 | //! \param B_n1 = B(+dV) and \param B_n2 = (-dV) 43 | void BernoulliFnc_n(const std::vector &V); 44 | 45 | void set_main_diag(); 46 | void set_upper_diag(); 47 | void set_lower_diag(); 48 | void set_rhs(const std::vector &Un); 49 | }; 50 | 51 | #endif // CONTINUITY_N_H 52 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v2.0/continuity_p.h: -------------------------------------------------------------------------------- 1 | #ifndef CONTINUITY_P_H 2 | #define CONTINUITY_P_H 3 | 4 | #include 5 | #include "parameters.h" //needs this to know what parameters is 6 | #include "constants.h" 7 | 8 | class Continuity_p 9 | { 10 | public: 11 | Continuity_p(const Parameters ¶ms); 12 | 13 | //!Sets up the matrix equation Ap*p = bp for continuity equation for holes. 14 | //!\param V stores the voltage and is needed to calculate Bernoulli fnc.'s. 15 | //!\param Up stores the net generation rate, needed for the right hand side. 16 | void setup_eqn(const std::vector &V, const std::vector & Up); 17 | 18 | //getters 19 | std::vector get_main_diag() const {return main_diag;} 20 | std::vector get_upper_diag() const {return upper_diag;} 21 | std::vector get_lower_diag() const {return lower_diag;} 22 | std::vector get_rhs() const {return rhs;} 23 | std::vector get_p_mob() const {return p_mob;} 24 | std::vector get_B_p1() const {return B_p1;} 25 | std::vector get_B_p2() const {return B_p2;} 26 | double get_p_leftBC() const {return p_leftBC;} 27 | double get_p_rightBC() const {return p_rightBC;} 28 | 29 | private: 30 | std::vector main_diag; 31 | std::vector upper_diag; 32 | std::vector lower_diag; 33 | std::vector rhs; 34 | std::vector p_mob; 35 | std::vector B_p1; //bernoulli (+dV) 36 | std::vector B_p2; //bernoulli (-dV) 37 | double Cp; 38 | double p_leftBC; 39 | double p_rightBC; 40 | 41 | //!Calculates the Bernoulli functions and updates member arrays 42 | //! \param B_n1 = B(+dV) and \param B_n2 = (-dV) 43 | void BernoulliFnc_p(const std::vector &V); 44 | 45 | void set_main_diag(); 46 | void set_upper_diag(); 47 | void set_lower_diag(); 48 | void set_rhs(const std::vector &Up); 49 | }; 50 | 51 | #endif // CONTINUITY_P_H 52 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v2.0/make: -------------------------------------------------------------------------------- 1 | # Comment lines 2 | # Here we define compiler option, libraries and the target 3 | FLAGS = -O2 -Wall 4 | 5 | # Here we make the executable file 6 | SRCS = main.cpp bernoulli.cpp continuity_n.cpp continuity_p.cpp parameters.cpp photogeneration.cpp poisson.cpp recombination.cpp thomas_tridiag_solve.cpp 7 | OBJS = $(subst .cpp,.o,$(SRCS)) 8 | all: 1D_DD 9 | 10 | # Whereas here we create the object file 11 | DD: $(OBJS) constants.h 12 | g++ ${FLAGS} -o 1D_DD $(OBJS) 13 | 14 | %.o: %.cpp constants.h 15 | g++ $(FLAGS) -c $< 16 | 17 | # Clean 18 | clean: 19 | rm *.o ./1D_DD 20 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v2.0/parameters.h: -------------------------------------------------------------------------------- 1 | #ifndef PARAMETERS_H 2 | #define PARAMETERS_H 3 | 4 | #include "constants.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | struct Parameters //parameters need to be accessble, so all members are public. 13 | { 14 | Parameters(){} 15 | void reduce_w(){w = w/w_reduce_factor;} 16 | void relax_tolerance(){tolerance = tolerance*tol_relax_factor;} 17 | void use_tolerance_eq() {tolerance = tolerance_eq;} 18 | void use_tolerance_i() {tolerance = tolerance_i;} 19 | void use_w_i() {w = w_i;} 20 | void use_w_eq() {w = w_eq;} 21 | 22 | void Initialize(); 23 | void isPositive(double input, const std::string &comment); 24 | void isPositive(int input, const std::string &comment); 25 | void isNegative(double input, const std::string &comment); 26 | void isNegative(int input, const std::string &comment); 27 | 28 | double N_LUMO, N_HOMO, phi_a, phi_c, eps_active, p_mob_active, n_mob_active; 29 | double dx, mobil; 30 | double E_gap, active_CB, active_VB, WF_anode, WF_cathode, N, Nsqrd; 31 | double Photogen_scaling, k_rec; 32 | 33 | double w; 34 | double w_reduce_factor; 35 | double tolerance, tolerance_eq; 36 | double tol_relax_factor; 37 | double Vmin, Vmax; 38 | 39 | double tolerance_i, w_i, w_eq; 40 | double L; 41 | int num_cell; 42 | std::string GenRateFileName; 43 | double Va_min, Va_max, increment; 44 | 45 | }; 46 | 47 | #endif // PARAMETERS_H 48 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v2.0/parameters.inp: -------------------------------------------------------------------------------- 1 | //NOTE:IF-HAVE-ANY-SPACES-IN-COMMENTS-IT-WILL-FAIL 2 | 300.0e-9 //device-thickness(m) 3 | 1e24 //N-LUMO 4 | 1e24 //N-HOMO 5 | 7e27 //Photogeneration-scaling 6 | 0.2 //phi_a 7 | 0.1 //phi_c 8 | 3.0 //eps_active 9 | 4.5e-6 //p_mob_active 10 | 4.5e-6 //n_mob_active 11 | 5e-6 //mobil 12 | 1.5 //E_gap 13 | -3.9 //active_CB 14 | -5.4 //active_VB 15 | 4.8 //WF_anode 16 | 3.7 //WF_cathode 17 | 6e-17 //k_rec 18 | 19 | 1.0e-9 //dx 20 | 21 | -0.5 //Va_min 22 | 1.2 //Va_max 23 | 0.01 //increment 24 | 0.01 //w_eq 25 | 0.2 //w_i 26 | 5e-12 //tolerance_i 27 | 2.0 //w_reduce_factor 28 | 10.0 //tol_relax_factor 29 | gen_rate.inp //GenRateFileName 30 | 31 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v2.0/photogeneration.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "photogeneration.h" 5 | 6 | //constructor definition 7 | Photogeneration::Photogeneration(const Parameters ¶ms, double photogen_scaling, const std::string gen_rate_file_name){ 8 | 9 | PhotogenRate.resize(params.num_cell); 10 | PhotogenRate_max = photogen_scaling; 11 | 12 | 13 | std::ifstream GenRateFile; 14 | 15 | GenRateFile.open(gen_rate_file_name); 16 | //check if file was opened 17 | if (!GenRateFile) { 18 | std::cerr << "Unable to open file gen_rate.txt"; 19 | exit(1); // call system to stop 20 | } 21 | 22 | for (int i = 1; i <= params.num_cell-1; i++) { 23 | GenRateFile >> PhotogenRate[i]; 24 | //std::cout << "G(i) " << G[i] < 5 | #include "constants.h" 6 | #include "parameters.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | class Photogeneration 13 | { 14 | public: 15 | //!Constructor will get the generation rate from file. 16 | //!Generation rate file should contain num_cell -2 number of entries in a single column, corresponding to 17 | //!the the generation rate at each mesh point (except the endpoints). 18 | //! \param photogen_scaling is the scaling factor obtained from fit to get the correct short-circuit current. 19 | Photogeneration(const Parameters ¶ms, double photogen_scaling, const std::string gen_rate_file_name); 20 | 21 | std::vector getPhotogenRate() {return PhotogenRate;} 22 | private: 23 | std::vector PhotogenRate; 24 | double PhotogenRate_max; 25 | }; 26 | 27 | 28 | 29 | #endif // PHOTOGENERATION_H 30 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v2.0/poisson.cpp: -------------------------------------------------------------------------------- 1 | #include "poisson.h" 2 | #include 3 | 4 | Poisson::Poisson(const Parameters ¶ms) 5 | { 6 | main_diag.resize(params.num_cell); 7 | upper_diag.resize(params.num_cell-1); 8 | lower_diag.resize(params.num_cell-1); 9 | rhs.resize(params.num_cell); 10 | epsilon.resize(params.num_cell +1); 11 | 12 | CV = params.N*params.dx*params.dx*q/(epsilon_0*Vt); 13 | std::fill(epsilon.begin(), epsilon.end(), params.eps_active); 14 | } //constructor) 15 | 16 | 17 | void Poisson::setup_matrix() //Note: this is on purpose different than the setup_eqn used for Continuity eqn's, b/c I need to setup matrix only once 18 | { 19 | set_main_diag(); 20 | set_lower_diag(); 21 | set_upper_diag(); 22 | } 23 | 24 | 25 | //---------------Setup AV diagonals (Poisson solve)--------------------------------------------------------------- 26 | //this is a in tridiag_solver 27 | void Poisson::set_main_diag() 28 | { 29 | for (int i = 1; i < main_diag.size(); i++) { 30 | main_diag[i] = -2.*epsilon[i]; 31 | } 32 | } 33 | 34 | //this is b in tridiag_solver 35 | void Poisson::set_upper_diag() 36 | { 37 | for (int i = 1; i < upper_diag.size(); i++) { 38 | upper_diag[i] = epsilon[i]; 39 | } 40 | } 41 | 42 | //this is c in tridiag_solver 43 | void Poisson::set_lower_diag() 44 | { 45 | for (int i = 1; i < lower_diag.size(); i++) { 46 | lower_diag[i] = epsilon[i]; 47 | } 48 | } 49 | 50 | //------------------------------------------------------------------------------------ 51 | void Poisson::set_rhs(const std::vector &n, const std::vector &p, double V_leftBC, double V_rightBC) 52 | { 53 | for (int i = 1; i <= rhs.size()-1; i++) { 54 | rhs[i] = CV*(n[i] - p[i]); 55 | //std::cout << "bV " << bV[i] << std::endl; 56 | } 57 | rhs[1] -= epsilon[0]*V_leftBC; 58 | rhs[rhs.size()-1] -= epsilon[rhs.size()]*V_rightBC; 59 | } 60 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v2.0/poisson.h: -------------------------------------------------------------------------------- 1 | #ifndef POISSON_H 2 | #define POISSON_H 3 | 4 | #include 5 | #include "parameters.h" //needs this to know what paramsation is 6 | #include "constants.h" 7 | 8 | class Poisson 9 | { 10 | public: 11 | Poisson(const Parameters ¶ms); 12 | 13 | //!Setup Poisson equation matrix AV. Note that the matrix will usually be the same throughout the simulation 14 | //! since it only depends on dielectric constant, so this function should be called only once. 15 | void setup_matrix(); 16 | 17 | //!Setup the right hand side of Poisson equation. This depends on the electron density \param n, 18 | //! hole density \param p, and left and right boundary conditions \param V_leftBC and \param V_rightBC 19 | void set_rhs(const std::vector &n, const std::vector &p, double V_leftBC, double V_rightBC); 20 | 21 | //getters 22 | std::vector get_main_diag() const {return main_diag;} 23 | std::vector get_upper_diag() const {return upper_diag;} 24 | std::vector get_lower_diag() const {return lower_diag;} 25 | std::vector get_rhs() const {return rhs;} 26 | 27 | private: 28 | std::vector main_diag; 29 | std::vector upper_diag; 30 | std::vector lower_diag; 31 | std::vector rhs; 32 | std::vector epsilon; 33 | double CV; //relative permitivity was moved into the matrix 34 | 35 | void set_main_diag(); 36 | void set_upper_diag(); 37 | void set_lower_diag(); 38 | }; 39 | 40 | #endif // POISSON_H 41 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v2.0/recombination.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "recombination.h" 4 | 5 | Recombo:: Recombo(const Parameters ¶ms) //constructor 6 | { 7 | k_rec = params.k_rec; 8 | R_Langevin.resize(params.num_cell); 9 | E_trap = params.active_VB + params.E_gap/2.0; //trap assisted recombo is most effective when trap is located mid-gap--> take VB and add 1/2 of bandgap 10 | n1 = params.N_LUMO*exp(-(params.active_CB - E_trap)/Vt); 11 | p1 = params.N_HOMO*exp(-(E_trap - params.active_VB)/Vt); 12 | } 13 | 14 | std::vector Recombo::ComputeR_Langevin(const Parameters ¶ms, const std::vector &n, const std::vector &p) 15 | { 16 | for (int i = 1; i < p.size(); i++) { 17 | R_Langevin[i] = k_rec*(params.N*params.N*n[i]*p[i] - n1*p1); 18 | if (R_Langevin[i] < 0.0) R_Langevin[i] = 0.0; //negative recombo is unphysical 19 | } 20 | 21 | return R_Langevin; 22 | } 23 | 24 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v2.0/recombination.h: -------------------------------------------------------------------------------- 1 | #ifndef RECOMBINATION_H 2 | #define RECOMBINATION_H 3 | 4 | #include 5 | #include "constants.h" 6 | #include "parameters.h" 7 | 8 | class Recombo{ 9 | public: 10 | //!Constructor sets up the recombination member parameters using the input from \param params 11 | Recombo(const Parameters ¶ms); 12 | 13 | //!Computes bimolecular Langevin recombination rate. 14 | //! This depends on the electron \param n and hole \param p density. 15 | std::vector ComputeR_Langevin(const Parameters ¶ms, const std::vector &n, const std::vector &p); 16 | 17 | private: 18 | double k_rec; //!bimolecular recombination coefficient 19 | double E_trap; //!Trap level energy 20 | double n1; 21 | double p1; 22 | std::vector R_Langevin; //!store the Langevin recombination rate 23 | }; 24 | 25 | 26 | #endif // RECOMBINATION_H 27 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v2.0/thomas_tridiag_solve.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | //!This function uses Thomas algorithm for tridiagonal matrix (special case of Gaussian elimination) 5 | //! diagonal = array containing elements of main diagonal. indices: (a1.....an) 6 | //! b = array containing elements of upper diagonal. indices (b1....b_n-1) 7 | //!c = array containing elements of lower diagonal. indices (c1...c_n-1) 8 | 9 | //!\author Timofey Golubev 10 | 11 | std::vector Thomas_solve(const std::vector &a,const std::vector &b,const std::vector &c, std::vector &rhs){ 12 | 13 | int num_elements = a.size()-1; //matrices indexed from 0, but we use it from 1 here 14 | 15 | double cdiag_ratio; 16 | std::vector x(num_elements+2); 17 | std::vector diagonal = a; //this is so the actual a value is unchanged, so can precalculate a before the loop... 18 | 19 | 20 | //Forward substitution 21 | for (int i = 2; i <= num_elements; i++) { 22 | cdiag_ratio = c[i-1]/diagonal[i-1]; //i-1 b/c need to use c1 and a1, and i starts at 2... 23 | diagonal[i] -= cdiag_ratio*b[i-1]; 24 | rhs[i] -= cdiag_ratio*rhs[i-1]; 25 | } 26 | 27 | 28 | 29 | //Backward substitution 30 | x[num_elements] = rhs[num_elements]/diagonal[num_elements]; //linear eqn corresponding to last row 31 | for (int i = num_elements; i > 1; i--) 32 | x[i-1] = (rhs[i-1] - x[i]*b[i-1])/diagonal[i-1]; 33 | 34 | return x; 35 | } 36 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/C++ implementation/v2.0/thomas_tridiag_solve.h: -------------------------------------------------------------------------------- 1 | #ifndef THOMAS_TRIDIAG_SOLVE_H 2 | #define THOMAS_TRIDIAG_SOLVE_H 3 | 4 | #include 5 | 6 | //!This function uses Thomas algorithm for tridiagonal matrix (special case of Gaussian elimination) 7 | //! diagonal = array containing elements of main diagonal. indices: (a1.....an) 8 | //! b = array containing elements of upper diagonal. indices (b1....b_n-1) 9 | //!c = array containing elements of lower diagonal. indices (c1...c_n-1) 10 | std::vector Thomas_solve(const std::vector &a, const std::vector &b,const std::vector &c, std::vector &rhs); 11 | 12 | 13 | 14 | #endif // THOMAS_TRIDIAG_SOLVE_H 15 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/Matlab implementation/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | *.smod 19 | 20 | # Compiled Static libraries 21 | *.lai 22 | *.la 23 | *.a 24 | *.lib 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | 31 | #data output 32 | *.txt 33 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/Matlab implementation/BernoulliFnc_n.m: -------------------------------------------------------------------------------- 1 | function Bn = BernoulliFnc_n(fullV) 2 | 3 | global num_cell 4 | 5 | dV = zeros(num_cell+1); 6 | Bn = zeros(2, num_cell+1); 7 | 8 | %Bernoulli fnc. 9 | 10 | for i = 2:num_cell+1 11 | dV(i) = fullV(i)-fullV(i-1); 12 | end 13 | 14 | for i = 2:num_cell+1 15 | Bn(1,i) = dV(i)/(exp(dV(i))-1.0); %B(+dV) 16 | Bn(2,i) = Bn(1,i)*exp(dV(i)); %B(-dV) 17 | end 18 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/Matlab implementation/BernoulliFnc_p.m: -------------------------------------------------------------------------------- 1 | function Bp = BernoulliFnc_p(fullV) 2 | 3 | global num_cell 4 | 5 | dV = zeros(num_cell+1); 6 | Bp = zeros(2, num_cell+1); 7 | 8 | %Bernoulli fnc. 9 | 10 | for i = 2:num_cell+1 11 | dV(i) = fullV(i)-fullV(i-1); 12 | end 13 | 14 | for i = 2:num_cell+1 15 | Bp(1,i) = dV(i)/(exp(dV(i))-1.0); %B(+dV) 16 | Bp(2,i) = Bp(1,i)*exp(dV(i)); %B(-dV) 17 | end 18 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/Matlab implementation/GenerationRate.m: -------------------------------------------------------------------------------- 1 | function G = GenerationRate() 2 | 3 | global G_max num_cell 4 | 5 | %Here can use any relevant analytic expression or input data from a file 6 | % i.e. 7 | %G = load('gen_rate.inp'); %skip the endpoints b/c we don't need them in Un.... 8 | 9 | G(2:num_cell) = 1; 10 | 11 | G = G_max*G(2:num_cell)/max(G(2:num_cell)); %normalize it 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/Matlab implementation/README.md: -------------------------------------------------------------------------------- 1 | # Drift-Diffusion_models 2 | 3 | This solves the semiconductor drift-diffusion equations in 1D for both electrons and holes using finite differences. The equations are Poisson eqn, 4 | continuity equation, and drift-diffusion equation which are solved in a decoupled iterative method (Gummel method). Scharfetter-Gummel 5 | discretization as well as linear mixing of old and new solutions is used to maintain stability. 6 | 7 | The code was originally developed for solar cells, but can be used for any semiconductor devices. 8 | 9 | Run the model using DD_2carriers_Main. The files plot_carrier_densities.m and plot_JVs.m can be used to plot the results. 10 | (Results will be saved to the current Matlab directory). 11 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/Matlab implementation/R_Langevin.m: -------------------------------------------------------------------------------- 1 | function R_Langevin_array = R_Langevin(n_full, p_full) 2 | global k_rec n1 p1 num_cell N 3 | 4 | R_Langevin_array = zeros(num_cell,1); 5 | 6 | for i = 2:num_cell 7 | R_Langevin_array(i,1) = k_rec*(N*n_full(i)*N*p_full(i) - n1*p1); 8 | 9 | %negative recombo rates are not physical 10 | if R_Langevin_array(i) < 0 11 | R_Langevin_array(i) = 0; 12 | end 13 | end 14 | 15 | R_Langevin_array = R_Langevin_array(2:num_cell); %to make sure aligned with G 16 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/Matlab implementation/SetAV_val.m: -------------------------------------------------------------------------------- 1 | function AV_val = SetAV_val(epsilon) 2 | global num_elements nx l_HTL_int l_ETL_int 3 | 4 | AV = zeros(nx-2,3); 5 | 6 | for i=1:num_elements 7 | AV(i,2) = -2.*epsilon(i+1); %i+1 b/c we fill matrix only corresponding to elements INSIDE the device 8 | end 9 | 10 | for i = 1:num_elements-1 11 | AV(i,1) = epsilon(i+1); %1st element here corresponds to 2nd row of matrix 12 | end 13 | AV(num_elements, 1) = 0; %last element is unused 14 | 15 | for i = 2:num_elements 16 | AV(i,3) = epsilon(i+1); %1st element here corresponds to the 1st row of matrix: so need to use i.e. epsilon corresponding to fullV(3) 17 | end 18 | 19 | 20 | AV(1,3) = 0; %1st element of Ap(:,3) is unused 21 | 22 | AV_val = spdiags(AV,-1:1,num_elements,num_elements); %A = spdiags(B,d,m,n) creates an m-by-n sparse matrix by taking the columns of B and placing them along the diagonals specified by d. 23 | 24 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/Matlab implementation/SetAn_val.m: -------------------------------------------------------------------------------- 1 | function An_val = SetAn_val(B_n) 2 | global num_elements n_mob 3 | 4 | An = zeros(num_elements,3); 5 | 6 | %last entry of An(:,1) 1st entry of 7 | %An(:,3) are unused b/c off diagonals have 1 less element than main 8 | %diagonal 9 | 10 | for i=1:num_elements 11 | An(i,2) = -(n_mob(i+1)*B_n(1,i+1) + n_mob(i+1+1)*B_n(2,i+1+1)); 12 | end 13 | for i = 1:num_elements-1 14 | An(i,1) = n_mob(i+1+1)*B_n(2,i+1+1); %should be i+1+1 b/c have B(dVi)*p_i-1 15 | end 16 | An(num_elements, 1) = 0; %last element is unused 17 | for i = 2:num_elements 18 | An(i,3) = n_mob(i+1)*B_n(1,i+1); %1st element here corresponds to the 1st row of matrix: so need to use B3 --> corresponding to p(3) 19 | end 20 | An(1,3) = 0; %1st element of An(:,3) is unused 21 | 22 | 23 | An_val = spdiags(An,-1:1,num_elements,num_elements); -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/Matlab implementation/SetAp_val.m: -------------------------------------------------------------------------------- 1 | function Ap_val = SetAp_val(B_p) 2 | global num_elements p_mob 3 | 4 | Ap = zeros(num_elements,3); 5 | 6 | % last entry of Ap(:,1) 1st entry of 7 | %Ap(:,3) are unused b/c off diagonals have 1 less element than main 8 | %diagonal! 9 | 10 | %NOTE: Mobilities indices just correspond to the the dV indices which are 11 | %same as B indices 12 | 13 | for i=1:num_elements 14 | Ap(i,2) = -(p_mob(i+1)*B_p(2,i+1) + p_mob(i+1+1)*B_p(1,i+1+1)); 15 | end 16 | 17 | for i = 1:num_elements-1 18 | Ap(i,1) = p_mob(i+1+1)*B_p(1,i+1+1); %should be i+1+1 b/c have B(dVi)*p_i-1 19 | end 20 | Ap(num_elements, 1) = 0; %last element is unused 21 | for i = 2:num_elements 22 | Ap(i,3) = p_mob(i+1)*B_p(2,i+1); %1st element here corresponds to the 1st row of matrix: so need to use B3 --> corresponding to p(3)--> I mean fullp(3) here 23 | end 24 | 25 | Ap(1,3) = 0; %1st element of Ap(:,3) is unused 26 | 27 | 28 | Ap_val = spdiags(Ap,-1:1,num_elements,num_elements); 29 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/Matlab implementation/Setbn.m: -------------------------------------------------------------------------------- 1 | function bn = Setbn(B_n, n_full, Un) 2 | global Cn num_cell num_elements n_mob; 3 | 4 | bn = -Cn*Un; 5 | 6 | %enforce boundary conditions through bp 7 | bn(1,1) = bn(1,1) - n_mob(2)*B_n(2,2)*n_full(1); 8 | bn(num_elements,1) = bn(num_elements,1) - n_mob(num_cell +1)*B_n(1,num_cell+1)*n_full(num_cell+1); 9 | 10 | 11 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/Matlab implementation/Setbp.m: -------------------------------------------------------------------------------- 1 | function bp = Setbp(B, p_full, Up) 2 | global Cp num_cell num_elements p_mob; 3 | 4 | bp = -Cp*Up; 5 | 6 | %enforce boundary conditions through bp 7 | bp(1,1) = bp(1,1) - p_mob(2)*B(1,2)*p_full(1); 8 | bp(num_elements, 1) = bp(num_elements,1) - p_mob(num_cell+1)*B(2,num_cell+1)*p_full(num_cell+1); 9 | 10 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/Matlab implementation/plot_JVs.m: -------------------------------------------------------------------------------- 1 | %Allows to plot multiply JV files on 1 graph and calculates the solar cell device 2 | %efficiency based on 1 Sun illumination. 3 | % For example can run DD_2carriers multiple times with different parameters 4 | % and save the JV output files. Then use this script to plot. 5 | 6 | clear all 7 | 8 | [File,Path]=uigetfile('*.txt','MultiSelect','on'); 9 | 10 | N = numel(File); 11 | 12 | for i = 1:N 13 | 14 | name= File(1,i); 15 | str=sprintf('%s', [Path name{1}]); 16 | format shortG %change formating so doesn't show 0's for e-11 values. 17 | data = importdata(str); 18 | 19 | V(:,i) = data(:,1); 20 | J(:,i) = data(:,2)/10; %the /10 is to convert A/m^2 to mA/cm^2 21 | 22 | for j = 1:size(J,1) 23 | JV(j) = 10*J(j,i)*V(j,i); %i is defining WHICH JV curve are working with 24 | end 25 | [maxPower, I] = min(JV) %use min--> b/c in solar cell working regime, my J/s are defined as negative, 26 | %I is the index where the min occurs 27 | 28 | maxPower = abs(maxPower) 29 | %max power points 30 | Jmpp = J(I,i) 31 | Vmpp = V(I,i) 32 | 33 | %incident power is considered to be 1000 W/m^2 ?? --> is the standard 34 | %1 Sun illumination 35 | PCE = (maxPower/1000)*100 36 | 37 | plot(V(:,i),J(:,i), 'LineWidth',1.5); 38 | hold on 39 | 40 | end 41 | 42 | matlab.graphics.internal.setPrintPreferences('DefaultPaperPositionMode','manual') 43 | set(gca, 'FontSize', 24) 44 | xlabel('Voltage (V)','interpreter','latex','FontSize',26.4); 45 | ylabel({'Current Density ($mA/cm^2$)'},'interpreter','latex','FontSize',26.4); 46 | 47 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/Matlab implementation/plot_carrier_densities.m: -------------------------------------------------------------------------------- 1 | %Allows to plot multiple charge densities 2 | %Running, will bring up a window where can select the files to plot. Takes 3 | %in the input files which are named i.e. 0.77V.txt 4 | 5 | clear all 6 | 7 | [File,Path]=uigetfile('*.txt','MultiSelect','on'); 8 | 9 | N = numel(File); 10 | 11 | figure; 12 | 13 | h = gobjects(1,N); 14 | 15 | for i = 1:N 16 | 17 | name= File(1,i); 18 | str=sprintf('%s', [Path name{1}]); 19 | format shortG %change formating so doesn't show 0's for e-11 values. 20 | 21 | data = importdata(str); 22 | 23 | x = data(:,1); %positions 24 | p = data(:,3); %hole densities 25 | n = data(:,4); %electron densities 26 | 27 | h(2*i-1) = semilogy(x,n, 'LineWidth',1.,'LineStyle', '--','Color','red'); 28 | hold on 29 | h(2*i) = semilogy(x,p, 'LineWidth',1.,'LineStyle', '--','Color','blue'); 30 | 31 | 32 | end 33 | 34 | matlab.graphics.internal.setPrintPreferences('DefaultPaperPositionMode','manual') 35 | set(gca, 'FontSize', 20) %20 allows to see more labels, on y axis which makes graph clearer 36 | xlabel('Position (m)','interpreter','latex','FontSize',26.4); 37 | ylabel({'Hole Density ($m^{-3}$)'},'interpreter','latex','FontSize',26.4); 38 | 39 | %legend labels just 2 curve types--> colors for n and p 40 | legend([h(1) h(N)],{'electrons', 'holes'}); 41 | 42 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/build-1D_general_DD-Desktop_Qt_5_8_0_MSVC2015_64bit-Debug/.qmake.stash: -------------------------------------------------------------------------------- 1 | QMAKE_DEFAULT_INCDIRS = \ 2 | "C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE" \ 3 | "C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\ATLMFC\\INCLUDE" \ 4 | "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt" \ 5 | "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.6.1\\include\\um" \ 6 | "C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\\\shared" \ 7 | "C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\\\um" \ 8 | "C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\\\winrt" 9 | QMAKE_DEFAULT_LIBDIRS = \ 10 | "C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\LIB\\amd64" \ 11 | "C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\ATLMFC\\LIB\\amd64" \ 12 | "C:\\Program Files (x86)\\Windows Kits\\10\\lib\\10.0.10240.0\\ucrt\\x64" \ 13 | "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.6.1\\lib\\um\\x64" \ 14 | "C:\\Program Files (x86)\\Windows Kits\\8.1\\lib\\winv6.3\\um\\x64" 15 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Drift-Diffusion/Two-charge-carriers/build-1D_general_DD-Desktop_Qt_5_8_0_MSVC2015_64bit-Release/.qmake.stash: -------------------------------------------------------------------------------- 1 | QMAKE_DEFAULT_INCDIRS = \ 2 | "C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE" \ 3 | "C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\ATLMFC\\INCLUDE" \ 4 | "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt" \ 5 | "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.6.1\\include\\um" \ 6 | "C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\\\shared" \ 7 | "C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\\\um" \ 8 | "C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\\\winrt" 9 | QMAKE_DEFAULT_LIBDIRS = \ 10 | "C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\LIB\\amd64" \ 11 | "C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\ATLMFC\\LIB\\amd64" \ 12 | "C:\\Program Files (x86)\\Windows Kits\\10\\lib\\10.0.10240.0\\ucrt\\x64" \ 13 | "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.6.1\\lib\\um\\x64" \ 14 | "C:\\Program Files (x86)\\Windows Kits\\8.1\\lib\\winv6.3\\um\\x64" 15 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/0pt05nmMesh_tridiag/E_200V__tridiag_0pt05nmMesh.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/0pt05nmMesh_tridiag/E_200V__tridiag_0pt05nmMesh.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/0pt05nmMesh_tridiag/E_200V_convergence_0pt05nmMesh.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/0pt05nmMesh_tridiag/E_200V_convergence_0pt05nmMesh.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/0pt05nmMesh_tridiag/JV_0pt05nmMesh_tridiag_result.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/0pt05nmMesh_tridiag/JV_0pt05nmMesh_tridiag_result.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/0pt05nmMesh_tridiag/J_200V_tridiag_0pt05nmMesh.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/0pt05nmMesh_tridiag/J_200V_tridiag_0pt05nmMesh.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/0pt05nmMesh_tridiag/p_200V_tridiag_0pt05nmMesh.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/0pt05nmMesh_tridiag/p_200V_tridiag_0pt05nmMesh.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/1nmMesh_tridiag/E.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/1nmMesh_tridiag/E.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/1nmMesh_tridiag/E_200V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/1nmMesh_tridiag/E_200V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/1nmMesh_tridiag/J.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/1nmMesh_tridiag/J.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/1nmMesh_tridiag/JV.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/1nmMesh_tridiag/JV.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/1nmMesh_tridiag/JV_10_to_20.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/1nmMesh_tridiag/JV_10_to_20.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/1nmMesh_tridiag/JV_from200V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/1nmMesh_tridiag/JV_from200V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/1nmMesh_tridiag/J_200V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/1nmMesh_tridiag/J_200V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/1nmMesh_tridiag/convergence_last_Va.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/1nmMesh_tridiag/convergence_last_Va.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/1nmMesh_tridiag/p.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/1nmMesh_tridiag/p.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/1nmMesh_tridiag/p_200V.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/1D/Single_Layer_Devices/Mott-Gurney_law/Benchmarks/1nmMesh_tridiag/p_200V.jpg -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Mott-Gurney_law/src/fluxSplit.m: -------------------------------------------------------------------------------- 1 | function [vp,vn] = fluxSplit(u,f,df,strategy) 2 | % WENO flux spliting subroutine. 3 | % OUTPUT: 4 | % * vp: positive flux, v^{+}, which corresponds to f_{i+1/2}^{-} 5 | % * vn: negative flux v^{-}, which corresponds to f_{i+1/2}^{+} 6 | 7 | switch strategy 8 | case{1} % Godunov - scalar fluxsplit (non-conservative) 9 | vp = f((u + abs(u))./2); %flux^{+} 10 | vn = f((u - abs(u))./2); %flux^{-} 11 | case{2} % Local Lax-Friedrichs 12 | v = f(u); alpha = abs(df(u)); 13 | vp = 0.5.*(v + alpha.*u); %flux^{+} 14 | vn = 0.5.*(v - alpha.*u); %flux^{-} 15 | case{3} % (Global) Lax-Friedrichs %This is most commonly used (see pg. 23 Shu paper: weno for convection dominated problems) 16 | v = f(u); alpha = max(abs(df(u))); 17 | vp = 0.5.*(v + alpha.*u); %flux^{+} 18 | vn = 0.5.*(v - alpha.*u); %flux^{-} 19 | otherwise 20 | error('only cases 1,2, and 3 are available') 21 | end -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Mott-Gurney_law/src/license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Manuel Diaz 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the distribution 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 18 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/Mott-Gurney_law/src/residual.m: -------------------------------------------------------------------------------- 1 | function dF = residual(u,flux,dflux,dx,nx,fluxsplit) 2 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 | % Compute the Residual using WENO5 4 | % 5 | % Residual = dF/dx 6 | % where F= is our WENO Flux across cells 7 | % 8 | % This is the WENO finite diff. approximation of the 1st derivative 9 | % 10 | % Slightly modified from original code by Manuel Diaz, NTU, 2012.08.20 11 | % 12 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 13 | 14 | % Flux Spliting 15 | [vp,vn] = fluxSplit(u,flux,dflux,fluxsplit); 16 | 17 | % Allocate arrays 18 | hn = zeros(size(vn)); hp = zeros(size(vp)); 19 | 20 | 21 | % Reconstruc h+ and h- fluxes 22 | for i = 3:nx-2 % from cell 3 to nx-2 23 | sr = i-2:i+2; % Stencil range 24 | [hn(i),hp(i-1)] = WENO5_1d_reconstruction(vp(sr),vn(sr)); 25 | end 26 | % Total flux 27 | h = hn + hp; 28 | 29 | % Set BCs 30 | %h(1:2) = 0; %to enforce conservation, h(2) is hi-1/2 for the x = 0 point. 31 | %h(nx-1:nx) = 0; % is hi+1/2 for the x=L point: note the i+1/2 are defined as hn(i) and i-1/2 as hp(i-1) 32 | 33 | % Set Periodic BC 34 | %h(1:2) = h(nx-4:nx-3); 35 | %h(nx-1:nx) = h(3:4); 36 | 37 | 38 | % Formulate Left and Right fluxes, equiv: % h(i)-h(i-1) 39 | h_right = h; h_left = circshift(h,[0,1]); 40 | 41 | % residual 42 | dF = (h_right - h_left)/dx; 43 | 44 | -------------------------------------------------------------------------------- /1D/Single_Layer_Devices/README.md: -------------------------------------------------------------------------------- 1 | # Drift-Diffusion_models 2 | 3 | 1D Drift Diffusion 4 | 5 | The Drift-Diffusion folder contains 1 and 2 carrier models. The 2 carrier version solves for the current-voltage curve of a solar cell under illumination. The 1 carrier version solves for the current-voltage curve for a material whose only free carrier are holes under an applied voltage in the dark. 6 | 7 | The 1D/Drift-Diffusion/Single-charge-carrier/src folder also contains an implementation using Slotboom variables, which is an alternative way to achieve stability without using Scharfetter Gummel discretization. 8 | 9 | ----------------------------------------------------------------------------------------------------------------------- 10 | The Mott-Gurney_law folder uses the weighted essentially non-oscillatory method (WENO) to solve for for the current-voltage curve for a material whose only free carrier are holes under an applied voltage in the dark in the Mott-Gurney limit (no diffusion current). 11 | 12 | The WENO implementation is based on original 1D wave eqn code by Manuel Diaz. 13 | 14 | WENO reference: Jiang & Shu; Efficient Implementation of Weighted ENO Schemes JCP. vol 126, 202-228 (1996) 15 | 16 | ----------------------------------------------------------------------------------------------------------------------- 17 | Sample results are located in the Benchmarks folders. 18 | -------------------------------------------------------------------------------- /2D/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | *.smod 19 | 20 | # Compiled Static libraries 21 | *.lai 22 | *.la 23 | *.a 24 | *.lib 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | 31 | #data output 32 | *.txt 33 | 34 | #QT builds 35 | *.stash 36 | *.pdb 37 | *.Debug 38 | *.Release 39 | Makefile 40 | *.ilk 41 | *.user 42 | -------------------------------------------------------------------------------- /2D/README.md: -------------------------------------------------------------------------------- 1 | 2D Drift Diffusion 2 | 3 | This solves the semiconductor drift-diffusion equations in 2D using finite differences. 4 | The "Two-charge-carriers" versions of the models currently solve for a solar cell under illumination. The "Single-charge-carrier" versions solve for the current-voltage curve of a material which only has holes as the free carrier and is under a varying applied voltage in the dark. All of the models can be modified to solve other systems (i.e. through changing the boundary conditions, adding recombination rates, and modifying the generation rate). 5 | 6 | The equations are Poisson eqn, 7 | continuity equation, and drift-diffusion equation which are solved in a decoupled iterative method (Gummel method). Scharfetter-Gummel 8 | discretization as well as linear mixing of old and new solutions is used to maintain stability. 9 | 10 | 11 | -------------------------------------------------------------------------------------------------------------- 12 | Requirements for C++ implementations: 13 | In addition to a C++11 compiler, the linear algebra library Eigen is needed. The library is 14 | composed of only header files, so no linking is necessary. One just needs to specify the directory of the library files in the include. Eigen can be downloaded at: http://eigen.tuxfamily.org/index.php?title=Main_Page 15 | 16 | Also the input files: "parameters.inp" and "gen_rate.inp" (for the 2 carrier versions) need to be located in the same directory as the source code. 17 | 18 | Also include the openmp compiler flag to allow for Eigen to parallelize the matrix solving. 19 | 20 | ------------------------------------------------ 21 | The Matlab implementations only require Matlab. 22 | -------------------------------------------------------------------------------- /2D/Single-charge-carrier/Matlab_implementation/Calculate_Bernoullis.m: -------------------------------------------------------------------------------- 1 | function Bernoulli_p_values = Calculate_Bernoullis(fullV) 2 | global num_cell 3 | 4 | %This will return a vector/array containing valuess for all 4 Bernoulli p fncs as follows: 5 | %Bernoulli_values = [Bp_posX Bp_negX Bp_posZ Bp_negZ] 6 | 7 | Bp_posX = zeros(num_cell+1,num_cell+1); 8 | Bp_negX = zeros(num_cell+1,num_cell+1); 9 | Bp_posZ = zeros(num_cell+1,num_cell+1); 10 | Bp_negZ = zeros(num_cell+1,num_cell+1); 11 | dV_X = zeros(num_cell+1,num_cell+1); 12 | dV_Z = zeros(num_cell+1,num_cell+1); 13 | 14 | for i = 2:num_cell+1 15 | for j = 2:num_cell+1 16 | dV_X(i,j) = fullV(i,j) - fullV(i-1,j); %NOTE: all the dV_X are ~0, b/c in x direction I made there be no difference in electric potential 17 | dV_Z(i,j) = fullV(i,j) - fullV(i,j-1); 18 | end 19 | end 20 | 21 | 22 | for i = 2:num_cell+1 23 | for j = 2:num_cell+1 24 | if(abs(dV_X(i,j)) < 10^-13) %to prevent blowup due to 0 denominator 25 | %taylor expand x/(e^x -1) 26 | Bp_posX(i,j) = 1;%1 - dV_X(i+1,j+1)/2 + (dV_X(i+1,j+1))^2/12 - (dV_X(i+1,j+1))^4/720; 27 | Bp_negX(i,j) = 1;%Bp_posX(i,j)*exp(dV_X(i+1,j+1)); 28 | else 29 | Bp_posX(i,j) = 1;%dV_X(i+1,j+1)/(exp(dV_X(i+1,j+1))-1.0); %the +1's b/c dV's are defined as from 2, and 1 corresponds to the boundary... 30 | Bp_negX(i,j) = 1;%Bp_posX(i,j)*exp(dV_X(i,j)); 31 | end 32 | if(abs(dV_Z(i,j)) < 10^-13) 33 | Bp_posZ(i,j) = 1;%1 - dV_X(i+1,j+1)/2 + (dV_X(i+1,j+1))^2/12 - (dV_X(i+1,j+1))^4/720; 34 | Bp_negZ(i,j) = 1;%Bp_posZ(i,j)*exp(dV_Z(i+1,j+1)); 35 | else 36 | Bp_posZ(i,j) = dV_Z(i,j)/(exp(dV_Z(i,j))-1.0); 37 | Bp_negZ(i,j) = Bp_posZ(i,j)*exp(dV_Z(i,j)); 38 | end 39 | end 40 | end 41 | 42 | %create a structure for this function to return all Bernoulli values 43 | Bernoulli_p_values.Bp_posX = Bp_posX; 44 | Bernoulli_p_values.Bp_negX = Bp_negX; 45 | Bernoulli_p_values.Bp_posZ = Bp_posZ; 46 | Bernoulli_p_values.Bp_negZ = Bp_negZ; 47 | -------------------------------------------------------------------------------- /2D/Single-charge-carrier/Matlab_implementation/SetbV_2D.m: -------------------------------------------------------------------------------- 1 | function bV = SetbV_2D(p_matrix, epsilon) 2 | global V_leftBC V_bottomBC V_topBC V_rightBC N CV; 3 | 4 | %set up rhs of Poisson equation. Note for epsilons, are assuming that 5 | %epsilons at the boundaries are the same as espilon cell into interior of 6 | %device 7 | 8 | 9 | index = 0; 10 | for j = 1:N 11 | if(j ==1) %different for 1st subblock 12 | for i = 1:N 13 | index = index +1; 14 | if (i==1) %1st element has 2 BC's. Note: pmatrix and nmatrix use only inner indices, but epsilon uses entire device, so need to +1 15 | bV(index,1) = CV*(p_matrix(i,j)) + epsilon(i+1,j+1)*(V_leftBC(1) + V_bottomBC); %RECALL matrix has +2 down diagonals, sign flipped from 1D version 16 | elseif (i==N) 17 | bV(index,1) = CV*(p_matrix(i,j)) + epsilon(i+1,j+1)*(V_rightBC(1) + V_bottomBC); 18 | else 19 | bV(index,1) = CV*(p_matrix(i,j)) + epsilon(i+1,j+1)*V_bottomBC; 20 | end 21 | end 22 | elseif(j == N) %different for last subblock 23 | for i = 1:N 24 | index = index +1; 25 | if (i==1) %1st element has 2 BC's 26 | bV(index,1) = CV*(p_matrix(i,j)) + epsilon(i+1,j+1)*(V_leftBC(N) + V_topBC); 27 | elseif (i==N) 28 | bV(index,1) = CV*(p_matrix(i,j)) + epsilon(i+1,j+1)*(V_rightBC(N) + V_topBC); 29 | else 30 | bV(index,1) = CV*(p_matrix(i,j)) + epsilon(i+1,j+1)*V_topBC; 31 | end 32 | end 33 | else %interior subblocks 34 | for i = 1:N 35 | index = index +1; 36 | if(i==1) 37 | bV(index,1) = CV*(p_matrix(i,j)) + epsilon(i+1,j+1)*V_leftBC(j); 38 | elseif(i==N) 39 | bV(index,1) = CV*(p_matrix(i,j)) + epsilon(i+1,j+1)*V_rightBC(j); 40 | else 41 | bV(index,1) = CV*(p_matrix(i,j)); 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /2D/Two-charge-carriers/C++_implementation/2D_DD.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | CONFIG += console c++11 3 | CONFIG -= app_bundle 4 | CONFIG -= qt 5 | 6 | QMAKE_CXXFLAGS += -openmp 7 | 8 | INCLUDEPATH += C:/Eigen 9 | DEPENDPATH += C:/Eigen 10 | 11 | SOURCES += main.cpp \ 12 | continuity_n.cpp \ 13 | continuity_p.cpp \ 14 | parameters.cpp \ 15 | photogeneration.cpp \ 16 | poisson.cpp \ 17 | recombination.cpp \ 18 | Utilities.cpp 19 | 20 | HEADERS += \ 21 | constants.h \ 22 | continuity_n.h \ 23 | continuity_p.h \ 24 | parameters.h \ 25 | photogeneration.h \ 26 | poisson.h \ 27 | recombination.h \ 28 | Utilities.h 29 | -------------------------------------------------------------------------------- /2D/Two-charge-carriers/C++_implementation/README.md: -------------------------------------------------------------------------------- 1 | # Drift-Diffusion_models 2 | 3 | 2D Drift Diffusion 4 | 5 | This solves the semiconductor drift-diffusion equations in 2D for both electrons and holes using finite differences. The equations are Poisson eqn, 6 | continuity equation, and drift-diffusion equation which are solved in a decoupled iterative method (Gummel method). Scharfetter-Gummel 7 | discretization as well as linear mixing of old and new solutions is used to maintain stability. 8 | 9 | The gen_rate.txt input file is needed for photogeneration.cpp, or can comment that section out and use an analytic expression or set it to zero if are not 10 | studying a device under illumination. The gen_rate.txt file should contain just 1 column of generation rates corresponding to each mesh point in the device (except the end points, x = 0 and x=L). An example generation rate file for device of 300nm thickness is included. 11 | 12 | The code was originally developed for solar cells, but can be used for any semiconductor devices. 13 | 14 | Code can be compiled using the makefile or the QT Creator .pro project file (just open that and run within QT). 15 | 16 | 17 | -------------------------------------------------------------------------------- /2D/Two-charge-carriers/C++_implementation/Utilities.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILITIES_H 2 | #define UTILITIES_H 3 | 4 | #include 5 | #include "parameters.h" 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | class Utilities 13 | { 14 | public: 15 | Utilities(); 16 | 17 | //!Applies linear mixing (result = w*new_value + (1-w)*old_value) where w is the mixing factor. 18 | //! The mixing factor is in the \param params object. 19 | std::vector linear_mix(const Parameters ¶ms,const std::vector &new_values,const std::vector &old_values); 20 | 21 | //!This writes to output files the details of voltage \param V, carrier densities \param p and \param n, current \param J_total, net electron generation rate \param Un. 22 | //! The files are named according to the applied voltage \param Va of this data. 23 | void write_details(const Parameters ¶ms, double Va, const Eigen::MatrixXd &V_matrix, const Eigen::MatrixXd &p_matrix, const Eigen::MatrixXd &n_matrix, const Eigen::MatrixXd &J_total_Z, const Eigen::MatrixXd &Un_matrix); 24 | 25 | //!Writes to a file the JV data. The file contains 3 columns, the applied voltage \param Va, the current \param J_total, 26 | //! and the number of iterations \param iter required to converge at that voltage. 27 | void write_JV(const Parameters ¶ms, std::ofstream &JV, double iter, double Va, const Eigen::MatrixXd &J_total_Z); 28 | 29 | }; 30 | 31 | #endif // UTILITIES_H 32 | -------------------------------------------------------------------------------- /2D/Two-charge-carriers/C++_implementation/constants.h: -------------------------------------------------------------------------------- 1 | #ifndef CONSTANTS_H 2 | #define CONSTANTS_H 3 | 4 | #include 5 | 6 | //Physical Constants (these should not be changed) (constexpr b/c known at compile-time) 7 | constexpr double q = 1.60217646e-19; //elementary charge, C 8 | constexpr double kb = 1.3806503e-23; //Boltzmann const., J/k 9 | constexpr double T = 296.; //temperature K 10 | constexpr double Vt = (kb*T)/q; //thermal voltage V 11 | constexpr double epsilon_0 = 8.85418782e-12; //F/m 12 | 13 | 14 | #endif // CONSTANTS_H 15 | -------------------------------------------------------------------------------- /2D/Two-charge-carriers/C++_implementation/gen_rate.inp: -------------------------------------------------------------------------------- 1 | 1.83560545e+22 2 | 1.82755381e+22 3 | 1.81954608e+22 4 | 1.81158192e+22 5 | 1.80366097e+22 6 | 1.79578290e+22 7 | 1.78794737e+22 8 | 1.78015404e+22 9 | 1.77240259e+22 10 | 1.76469268e+22 11 | 12 | -------------------------------------------------------------------------------- /2D/Two-charge-carriers/C++_implementation/parameters.h: -------------------------------------------------------------------------------- 1 | #ifndef PARAMETERS_H 2 | #define PARAMETERS_H 3 | 4 | #include "constants.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | struct Parameters //parameters need to be accessble, so all members are public. 13 | { 14 | Parameters(){} 15 | void reduce_w(){w = w/w_reduce_factor;} 16 | void relax_tolerance(){tolerance = tolerance*tol_relax_factor;} 17 | void use_tolerance_eq() {tolerance = tolerance_eq;} 18 | void use_tolerance_i() {tolerance = tolerance_i;} 19 | void use_w_i() {w = w_i;} 20 | void use_w_eq() {w = w_eq;} 21 | 22 | void Initialize(); 23 | void isPositive(double input, const std::string &comment); 24 | void isPositive(int input, const std::string &comment); 25 | void isNegative(double input, const std::string &comment); 26 | void isNegative(int input, const std::string &comment); 27 | 28 | double N_LUMO, N_HOMO, phi_a, phi_c, eps_active, p_mob_active, n_mob_active; 29 | double dx, mobil; 30 | double E_gap, active_CB, active_VB, WF_anode, WF_cathode, N_dos, Nsqrd; 31 | double Photogen_scaling, k_rec; 32 | 33 | double w; 34 | double w_reduce_factor; 35 | double tolerance, tolerance_eq; 36 | double tol_relax_factor; 37 | double Vmin, Vmax; 38 | 39 | double tolerance_i, w_i, w_eq; 40 | double L; 41 | int num_cell, num_elements; //num_elements = (num_cell-1)^2 42 | std::string GenRateFileName; 43 | double Va_min, Va_max, increment; 44 | double Vbi; 45 | 46 | 47 | }; 48 | 49 | #endif // PARAMETERS_H 50 | -------------------------------------------------------------------------------- /2D/Two-charge-carriers/C++_implementation/parameters.inp: -------------------------------------------------------------------------------- 1 | //NOTE:IF-HAVE-ANY-SPACES-IN-COMMENTS-IT-WILL-FAIL 2 | 10.0e-9 //device-thickness(m) 3 | 10 //num_cell 4 | 1e24 //N-LUMO 5 | 1e24 //N-HOMO 6 | 4e27 //Photogeneration-scaling 7 | 0.2 //phi_a 8 | 0.1 //phi_c 9 | 3.0 //eps_active 10 | 4.5e-6 //p_mob_active 11 | 4.5e-6 //n_mob_active 12 | 5e-6 //mobil 13 | 1.5 //E_gap 14 | -3.9 //active_CB 15 | -5.4 //active_VB 16 | 4.8 //WF_anode 17 | 3.7 //WF_cathode 18 | 6e-17 //k_rec 19 | 1.0e-9 //dx 20 | 21 | -0.5 //Va_min 22 | -0.45 //Va_max 23 | 0.01 //increment 24 | 0.2 //w_eq 25 | 0.2 //w_i 26 | 5e-12 //tolerance_i 27 | 2.0 //w_reduce_factor 28 | 10.0 //tol_relax_factor 29 | gen_rate.inp //GenRateFileName 30 | 31 | -------------------------------------------------------------------------------- /2D/Two-charge-carriers/C++_implementation/parameters_large_device.inp: -------------------------------------------------------------------------------- 1 | //NOTE:IF-HAVE-ANY-SPACES-IN-COMMENTS-IT-WILL-FAIL 2 | 300.0e-9 //device-thickness(m) 3 | 300 //num_cell 4 | 10e24 //N-LUMO 5 | 10e24 //N-HOMO 6 | 7e27 //Photogeneration-scaling 7 | 0.2 //phi_a 8 | 0.1 //phi_c 9 | 3.0 //eps_active 10 | 4.5e-6 //p_mob_active 11 | 4.5e-6 //n_mob_active 12 | 5e-6 //mobil 13 | 1.5 //E_gap 14 | -3.9 //active_CB 15 | -5.4 //active_VB 16 | 4.8 //WF_anode 17 | 3.7 //WF_cathode 18 | 6e-17 //k_rec 19 | 1.0e-9 //dx 20 | 21 | -0.5 //Va_min 22 | 1.2 //Va_max 23 | 0.01 //increment 24 | 0.01 //w_eq 25 | 0.2 //w_i 26 | 5e-12 //tolerance_i 27 | 2.0 //w_reduce_factor 28 | 10.0 //tol_relax_factor 29 | gen_rate.inp //GenRateFileName 30 | 31 | -------------------------------------------------------------------------------- /2D/Two-charge-carriers/C++_implementation/photogeneration.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "photogeneration.h" 5 | 6 | //constructor definition 7 | Photogeneration::Photogeneration(const Parameters ¶ms, double photogen_scaling, const std::string gen_rate_file_name){ 8 | 9 | PhotogenRate.resize(params.num_cell, params.num_cell); //need 0 through N indices, and N = num_cell-1 10 | PhotogenRate_max = photogen_scaling; 11 | 12 | std::vector Photogen_vector(params.num_cell); //temporary vector, to input gen rate from file 13 | 14 | std::ifstream GenRateFile; 15 | 16 | GenRateFile.open(gen_rate_file_name); 17 | //check if file was opened 18 | if (!GenRateFile) { 19 | std::cerr << "Unable to open file gen_rate.txt"; 20 | exit(1); // call system to stop 21 | } 22 | 23 | for (int i = 1; i <= params.num_cell-1; i++) { 24 | GenRateFile >> Photogen_vector[i]; 25 | } 26 | 27 | //----normalize the photogen rate-------------------------- 28 | 29 | double maxOfGPhotogenRate = *std::max_element(Photogen_vector.begin(),Photogen_vector.end()); 30 | 31 | for (int i= 1; i <= params.num_cell-1; i++) { 32 | Photogen_vector[i] = PhotogenRate_max*Photogen_vector[i]/maxOfGPhotogenRate; 33 | //std::cout << "G(i) " << G[i] < 5 | #include "constants.h" 6 | #include "parameters.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | class Photogeneration 15 | { 16 | public: 17 | 18 | //!Constructor will get the generation rate from file. 19 | //!Generation rate file should contain num_cell -2 number of entries in a single column, corresponding to 20 | //!the the generation rate at each mesh point (except the endpoints). 21 | //! \param photogen_scaling is the scaling factor obtained from fit to get the correct short-circuit current. 22 | Photogeneration(const Parameters ¶ms, double photogen_scaling, const std::string gen_rate_file_name); 23 | 24 | Eigen::MatrixXd getPhotogenRate() {return PhotogenRate;} 25 | 26 | private: 27 | Eigen::MatrixXd PhotogenRate; 28 | double PhotogenRate_max; 29 | }; 30 | 31 | 32 | 33 | #endif // PHOTOGENERATION_H 34 | -------------------------------------------------------------------------------- /2D/Two-charge-carriers/C++_implementation/recombination.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "recombination.h" 4 | 5 | Recombo:: Recombo(const Parameters ¶ms) //constructor 6 | { 7 | k_rec = params.k_rec; 8 | R_Langevin.resize(params.num_cell); 9 | E_trap = params.active_VB + params.E_gap/2.0; //trap assisted recombo is most effective when trap is located mid-gap--> take VB and add 1/2 of bandgap 10 | n1 = params.N_LUMO*exp(-(params.active_CB - E_trap)/Vt); 11 | p1 = params.N_HOMO*exp(-(E_trap - params.active_VB)/Vt); 12 | } 13 | 14 | std::vector Recombo::ComputeR_Langevin(const Parameters ¶ms, const std::vector &n, const std::vector &p) 15 | { 16 | for (int i = 1; i < p.size(); i++) { 17 | R_Langevin[i] = k_rec*(params.N_dos*params.N_dos*n[i]*p[i] - n1*p1); 18 | if (R_Langevin[i] < 0.0) R_Langevin[i] = 0.0; //negative recombo is unphysical 19 | } 20 | 21 | //for now just use n and p vectors.... 22 | 23 | return R_Langevin; 24 | } 25 | 26 | -------------------------------------------------------------------------------- /2D/Two-charge-carriers/C++_implementation/recombination.h: -------------------------------------------------------------------------------- 1 | #ifndef RECOMBINATION_H 2 | #define RECOMBINATION_H 3 | 4 | #include 5 | #include "constants.h" 6 | #include "parameters.h" 7 | 8 | class Recombo{ 9 | public: 10 | //!Constructor sets up the recombination member parameters using the input from \param params 11 | Recombo(const Parameters ¶ms); 12 | 13 | //!Computes bimolecular Langevin recombination rate. 14 | //! This depends on the electron \param n and hole \param p density. 15 | std::vector ComputeR_Langevin(const Parameters ¶ms, const std::vector &n, const std::vector &p); 16 | 17 | private: 18 | double k_rec; //!bimolecular recombination coefficient 19 | double E_trap; //!Trap level energy 20 | double n1; 21 | double p1; 22 | std::vector R_Langevin; //!store the Langevin recombination rate 23 | }; 24 | 25 | 26 | #endif // RECOMBINATION_H 27 | -------------------------------------------------------------------------------- /2D/Two-charge-carriers/C++_implementation/short_vector_of_vectors_test.cpp: -------------------------------------------------------------------------------- 1 | // Example program 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | int main() 8 | { 9 | 10 | std::vector > matrix; 11 | 12 | matrix.resize(10, std::vector(10)); 13 | 14 | std::cout << matrix[9].size() << std::endl; 15 | 16 | for (int i = 0; i < 10; i++) { 17 | std::fill(matrix[i].begin(), matrix[i].end(),5); 18 | } 19 | 20 | std::cout << matrix[8][4] << std::endl; 21 | } 22 | -------------------------------------------------------------------------------- /2D/Two-charge-carriers/Matlab_implementation/GenerationRate.m: -------------------------------------------------------------------------------- 1 | function G = GenerationRate() 2 | 3 | global G_max num_elements 4 | 5 | % G = ones(N,N); 6 | G = ones(num_elements,1); %better to use it as a 1D vector 7 | 8 | %Here can use any relevant analytic expression or input data from a file 9 | % i.e. 10 | %G(2:num_cell) = load('gen_rate.txt'); 11 | %G(x,z) 12 | % G(1:N,1:N) = 1; %these just include the insides--> since want Un and Up to just include the insides--> i,j directly correspond to the matrix indices 13 | 14 | G = G_max.*G; %skip the endpoints b/c we don't need them in Un... 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /2D/Two-charge-carriers/Matlab_implementation/SetbV_2D.m: -------------------------------------------------------------------------------- 1 | function bV = SetbV_2D(p_matrix, n_matrix, epsilon) 2 | global V_leftBC V_bottomBC V_topBC V_rightBC N CV; 3 | 4 | %set up rhs of Poisson equation. Note for epsilons, are assuming that 5 | %epsilons at the boundaries are the same as espilon cell into interior of 6 | %device 7 | 8 | 9 | index = 0; 10 | for j = 1:N 11 | if(j ==1) %different for 1st subblock 12 | for i = 1:N 13 | index = index +1; 14 | if (i==1) %1st element has 2 BC's. Note: pmatrix and nmatrix use only inner indices, but epsilon uses entire device, so need to +1 15 | bV(index,1) = CV*(p_matrix(i,j)-n_matrix(i,j)) + epsilon(i+1,j+1)*(V_leftBC(1) + V_bottomBC); %RECALL matrix has +2 down diagonals, sign flipped from 1D version 16 | elseif (i==N) 17 | bV(index,1) = CV*(p_matrix(i,j)-n_matrix(i,j)) + epsilon(i+1,j+1)*(V_rightBC(1) + V_bottomBC); 18 | else 19 | bV(index,1) = CV*(p_matrix(i,j)-n_matrix(i,j)) + epsilon(i+1,j+1)*V_bottomBC; 20 | end 21 | end 22 | elseif(j == N) %different for last subblock 23 | for i = 1:N 24 | index = index +1; 25 | if (i==1) %1st element has 2 BC's 26 | bV(index,1) = CV*(p_matrix(i,j)-n_matrix(i,j)) + epsilon(i+1,j+1)*(V_leftBC(N) + V_topBC); 27 | elseif (i==N) 28 | bV(index,1) = CV*(p_matrix(i,j)-n_matrix(i,j)) + epsilon(i+1,j+1)*(V_rightBC(N) + V_topBC); 29 | else 30 | bV(index,1) = CV*(p_matrix(i,j)-n_matrix(i,j)) + epsilon(i+1,j+1)*V_topBC; 31 | end 32 | end 33 | else %interior subblocks 34 | for i = 1:N 35 | index = index +1; 36 | if(i==1) 37 | bV(index,1) = CV*(p_matrix(i,j)-n_matrix(i,j)) + epsilon(i+1,j+1)*V_leftBC(j); 38 | elseif(i==N) 39 | bV(index,1) = CV*(p_matrix(i,j)-n_matrix(i,j)) + epsilon(i+1,j+1)*V_rightBC(j); 40 | else 41 | bV(index,1) = CV*(p_matrix(i,j)-n_matrix(i,j)); 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /2D/Two-charge-carriers/Matlab_implementation/Setbn_2D.m: -------------------------------------------------------------------------------- 1 | function bn = Setbn_2D(Bernoulli_n_values, n_mob, Un) 2 | global Cn num_elements n_topBC n_leftBC n_rightBC n_bottomBC N; 3 | 4 | % bn = zeros(num_elements,1); 5 | 6 | %extract variables from struct (for brevity in eqns) 7 | Bn_posX = Bernoulli_n_values.Bn_posX; 8 | Bn_negX = Bernoulli_n_values.Bn_negX; 9 | Bn_posZ = Bernoulli_n_values.Bn_posZ; 10 | Bn_negZ = Bernoulli_n_values.Bn_negZ; 11 | 12 | %calculate main part here 13 | bn = Cn*Un; 14 | 15 | %add on BC's 16 | index = 0; 17 | for j = 1:N 18 | if(j ==1) %different for 1st subblock 19 | for i = 1:N 20 | index = index +1; 21 | if (i==1) %1st element has 2 BC's 22 | bn(index,1) = bn(index,1) + n_mob(i+1,j+1)*(Bn_negX(i+1,j+1)*n_leftBC(1) + Bn_negZ(i+1,j+1)*n_bottomBC); %NOTE: rhs is +Cp*Un, b/c diagonal elements are + here, flipped sign from 1D version 23 | elseif (i==N) 24 | bn(index,1) = bn(index,1) + n_mob(i+1,j+1)*(Bn_negZ(i+1,j+1)*n_bottomBC + Bn_posX(i+1+1,j+1)*n_rightBC(1)); 25 | else 26 | bn(index,1) = bn(index,1) + n_mob(i+1,j+1)*Bn_negZ(i+1,j+1)*n_bottomBC; 27 | end 28 | end 29 | elseif(j == N) %different for last subblock 30 | for i = 1:N 31 | index = index +1; 32 | if (i==1) %1st element has 2 BC's 33 | bn(index,1) = bn(index,1) + n_mob(i+1,j+1)*(Bn_negX(i+1,j+1)*n_leftBC(N) + Bn_posZ(i+1,j+1+1)*n_topBC); 34 | elseif (i==N) 35 | bn(index,1) = bn(index,1) + n_mob(i+1,j+1)*(Bn_posX(i+1+1,j+1)*n_rightBC(N) + Bn_posZ(i+1,j+1+1)*n_topBC); 36 | else 37 | bn(index,1) = bn(index,1) + n_mob(i+1,j+1)*Bn_posZ(i+1,j+1+1)*n_topBC; 38 | end 39 | end 40 | else %interior subblocks 41 | for i = 1:N 42 | index = index +1; 43 | if(i==1) 44 | bn(index,1) = bn(index,1) + n_mob(i+1,j+1)*Bn_negX(i+1,j+1)*n_leftBC(j); 45 | elseif(i==N) 46 | bn(index,1) = bn(index,1) + n_mob(i+1,j+1)*Bn_posX(i+1+1,j+1)*n_rightBC(j); 47 | end 48 | end 49 | end 50 | end -------------------------------------------------------------------------------- /3D/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | *.smod 19 | 20 | # Compiled Static libraries 21 | *.lai 22 | *.la 23 | *.a 24 | *.lib 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | 31 | #data output 32 | *.txt 33 | 34 | #QT builds 35 | *.stash 36 | *.pdb 37 | *.Debug 38 | *.Release 39 | Makefile 40 | *.ilk 41 | *.user 42 | -------------------------------------------------------------------------------- /3D/C++_implementation/Single-charge-carrier/3D_DD_Cpp.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | CONFIG += console c++14 3 | CONFIG -= app_bundle 4 | CONFIG -= qt 5 | 6 | QMAKE_CXXFLAGS += -DMKL_LP64 \ #I think ENABLES MKL TO BE USED 7 | -openmp #this works!, b/c when commented out the -openmp flag, a thread call to eigen givess 1 instead of 8 when include this flag. 8 | 9 | 10 | #QMAKE_CXXFLAGS += -Ox #is full optimization in Msvc 11 | #makes no speed difference! 12 | 13 | INCLUDEPATH += C:/Eigen \ 14 | C:/IntelSWTools/compilers_and_libraries_2018.3.210/windows/mkl/include 15 | DEPENDPATH += C:/Eigen 16 | 17 | SOURCES += main.cpp \ 18 | continuity_p.cpp \ 19 | parameters.cpp \ 20 | poisson.cpp \ 21 | Utilities.cpp 22 | 23 | HEADERS += \ 24 | constants.h \ 25 | continuity_p.h \ 26 | parameters.h \ 27 | poisson.h \ 28 | Utilities.h 29 | 30 | LIBS += -L"C:/IntelSWTools/compilers_and_libraries_2018.3.210/windows/mkl/lib/intel64_win" -lmkl_intel_lp64 -lmkl_sequential -lmkl_core #NOTE: there must be no empty spaces between the -L and the path string! 31 | -------------------------------------------------------------------------------- /3D/C++_implementation/Single-charge-carrier/Mingw_version/Mingw_version.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | CONFIG += console c++11 3 | CONFIG -= app_bundle 4 | CONFIG -= qt 5 | 6 | QMAKE_CXXFLAGS += -fopenmp 7 | 8 | INCLUDEPATH += C:/Eigen 9 | DEPENDPATH += C:/Eigen 10 | 11 | SOURCES += \ 12 | ../continuity_p.cpp \ 13 | ../main.cpp \ 14 | ../parameters.cpp \ 15 | ../poisson.cpp \ 16 | ../Utilities.cpp 17 | 18 | HEADERS += \ 19 | ../constants.h \ 20 | ../continuity_p.h \ 21 | ../parameters.h \ 22 | ../poisson.h \ 23 | ../Utilities.h 24 | 25 | LIBS += -L C:/mingw/ -llibgomp #need to link this library for it to work with gcc 26 | -------------------------------------------------------------------------------- /3D/C++_implementation/Single-charge-carrier/Mingw_version/parameters.inp: -------------------------------------------------------------------------------- 1 | //NOTE:IF-HAVE-ANY-SPACES-IN-COMMENTS-IT-WILL-FAIL 2 | 80.01e-9 //device-lenght(m)X 3 | 80.01e-9 //device-WIDTH(m)Y 4 | 80.01e-9 //device-thickness(m)Z 5 | 2.0e-9 //dx_mesh_size 6 | 2.0e-9 //dy_mesh_size 7 | 2.0e-9 //dz_mesh_size 8 | 1.25e27 //N-LUMO 9 | 1.25e27 //N-HOMO 10 | 3.0 //eps_active 11 | 1e-8 //p_mob_active 12 | 1e-8 //mobil 13 | 14 | 0.1 //Va_min 15 | 1.0 //Va_max 16 | 0.1 //increment 17 | 0.2 //w_eq 18 | 0.2 //w_i 19 | 5e-12 //tolerance_i 20 | 2.0 //w_reduce_factor 21 | 10.0 //tol_relax_factor 22 | 23 | -------------------------------------------------------------------------------- /3D/C++_implementation/Single-charge-carrier/README.md: -------------------------------------------------------------------------------- 1 | # Drift-Diffusion_models 2 | 3 | 3D Drift Diffusion 4 | 5 | This solves the semiconductor drift-diffusion equations in 3D for both electrons and holes using finite differences. The equations are Poisson eqn, 6 | continuity equation, and drift-diffusion equation which are solved in a decoupled iterative method (Gummel method). Scharfetter-Gummel 7 | discretization as well as linear mixing of old and new solutions is used to maintain stability. 8 | 9 | The gen_rate.txt input file is needed for photogeneration.cpp, or can comment that section out and use an analytic expression or set it to zero if are not 10 | studying a device under illumination. The gen_rate.txt file should contain just 1 column of generation rates corresponding to each mesh point in the device (except the end points, x = 0 and x=L). An example generation rate file is included. 11 | 12 | The code was originally developed for solar cells, but can be used for any semiconductor devices. 13 | 14 | Code can be compiled using the makefile or the QT Creator .pro project file (just open that and run within QT). 15 | 16 | 17 | -------------------------------------------------------------------------------- /3D/C++_implementation/Single-charge-carrier/Utilities.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILITIES_H 2 | #define UTILITIES_H 3 | 4 | #include 5 | #include "parameters.h" 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | class Utilities 14 | { 15 | public: 16 | Utilities(); 17 | 18 | //!Applies linear mixing (result = w*new_value + (1-w)*old_value) where w is the mixing factor. 19 | //! The mixing factor is in the \param params object. The new_values and old_values are of Eigen::Tensor type (to allow reshaping later) and return value is 20 | //! also of Tensor type. 21 | Eigen::Tensor linear_mix(const Parameters ¶ms, const Eigen::Tensor &new_values, const Eigen::Tensor &old_values); 22 | 23 | //!This writes to output files the details of voltage \param V, carrier densities \param p and \param n, current \param J_total, net electron generation rate \param Un. 24 | //! The files are named according to the applied voltage \param Va of this data. 25 | void write_details(const Parameters ¶ms, double Va, const Eigen::Tensor &fullV, Eigen::Tensor &fullp, const Eigen::Tensor &J_total_Z, const std::vector &Up); 26 | 27 | //!Writes to a file the JV data. The file contains 3 columns, the applied voltage \param Va, the current \param J_total, 28 | //! and the number of iterations \param iter required to converge at that voltage. 29 | void write_JV(const Parameters ¶ms, std::ofstream &JV, double iter, double Va, const Eigen::Tensor &J_total_Z); 30 | 31 | }; 32 | 33 | #endif // UTILITIES_H 34 | -------------------------------------------------------------------------------- /3D/C++_implementation/Single-charge-carrier/build-Mingw_version-Desktop_Qt_5_11_1_MinGW_32bit-Debug/parameters.inp: -------------------------------------------------------------------------------- 1 | //NOTE:IF-HAVE-ANY-SPACES-IN-COMMENTS-IT-WILL-FAIL 2 | 30.01e-9 //device-lenght(m)X 3 | 30.01e-9 //device-WIDTH(m)Y 4 | 30.01e-9 //device-thickness(m)Z 5 | 2.0e-9 //dx_mesh_size 6 | 2.0e-9 //dy_mesh_size 7 | 2.0e-9 //dz_mesh_size 8 | 1.25e27 //N-LUMO 9 | 1.25e27 //N-HOMO 10 | 3.0 //eps_active 11 | 1e-8 //p_mob_active 12 | 1e-8 //mobil 13 | 14 | 0.1 //Va_min 15 | 1.0 //Va_max 16 | 0.1 //increment 17 | 0.2 //w_eq 18 | 0.2 //w_i 19 | 5e-12 //tolerance_i 20 | 2.0 //w_reduce_factor 21 | 10.0 //tol_relax_factor 22 | 23 | -------------------------------------------------------------------------------- /3D/C++_implementation/Single-charge-carrier/build-Mingw_version-Desktop_Qt_5_11_1_MinGW_32bit-Release/parameters.inp: -------------------------------------------------------------------------------- 1 | //NOTE:IF-HAVE-ANY-SPACES-IN-COMMENTS-IT-WILL-FAIL 2 | 10.01e-9 //device-lenght(m)X 3 | 10.01e-9 //device-WIDTH(m)Y 4 | 10.01e-9 //device-thickness(m)Z 5 | 2.0e-9 //dx_mesh_size 6 | 2.0e-9 //dy_mesh_size 7 | 2.0e-9 //dz_mesh_size 8 | 1.25e27 //N-LUMO 9 | 1.25e27 //N-HOMO 10 | 3.0 //eps_active 11 | 1e-8 //p_mob_active 12 | 1e-8 //mobil 13 | 14 | 0.1 //Va_min 15 | 1.0 //Va_max 16 | 0.1 //increment 17 | 0.2 //w_eq 18 | 0.2 //w_i 19 | 5e-12 //tolerance_i 20 | 2.0 //w_reduce_factor 21 | 10.0 //tol_relax_factor 22 | 23 | -------------------------------------------------------------------------------- /3D/C++_implementation/Single-charge-carrier/constants.h: -------------------------------------------------------------------------------- 1 | #ifndef CONSTANTS_H 2 | #define CONSTANTS_H 3 | 4 | #include 5 | 6 | //Physical Constants (these should not be changed) (constexpr b/c known at compile-time) 7 | constexpr double q = 1.60217646e-19; //elementary charge, C 8 | constexpr double kb = 1.3806503e-23; //Boltzmann const., J/k 9 | constexpr double T = 296.; //temperature K 10 | constexpr double Vt = (kb*T)/q; //thermal voltage V 11 | constexpr double epsilon_0 = 8.85418782e-12; //F/m 12 | 13 | 14 | #endif // CONSTANTS_H 15 | -------------------------------------------------------------------------------- /3D/C++_implementation/Single-charge-carrier/debug/untitled/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | int main() 6 | { 7 | cout << "Hello World!" << endl; 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /3D/C++_implementation/Single-charge-carrier/debug/untitled/untitled.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgolubev/Drift-Diffusion_models-Cpp_Matlab/3e3b3ee04747506a81fd06b3295b78df51c83333/3D/C++_implementation/Single-charge-carrier/debug/untitled/untitled.pro -------------------------------------------------------------------------------- /3D/C++_implementation/Single-charge-carrier/parameters.h: -------------------------------------------------------------------------------- 1 | #ifndef PARAMETERS_H 2 | #define PARAMETERS_H 3 | 4 | #include "constants.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | struct Parameters //parameters need to be accessble, so all members are public. 13 | { 14 | Parameters(){} 15 | void reduce_w(){w = w/w_reduce_factor;} 16 | void relax_tolerance(){tolerance = tolerance*tol_relax_factor;} 17 | void use_tolerance_eq() {tolerance = tolerance_eq;} 18 | void use_tolerance_i() {tolerance = tolerance_i;} 19 | void use_w_i() {w = w_i;} 20 | void use_w_eq() {w = w_eq;} 21 | 22 | void Initialize(); 23 | void isPositive(double input, const std::string &comment); 24 | void isPositive(int input, const std::string &comment); 25 | void isNegative(double input, const std::string &comment); 26 | void isNegative(int input, const std::string &comment); 27 | 28 | double N_LUMO, N_HOMO, eps_active, p_mob_active; 29 | double mobil; 30 | double N_dos, Nsqrd; 31 | //double Photogen_scaling, k_rec; 32 | 33 | double w; 34 | double w_reduce_factor; 35 | double tolerance, tolerance_eq; 36 | double tol_relax_factor; 37 | double Vmin, Vmax; 38 | 39 | double tolerance_i, w_i, w_eq; 40 | double Lx, Ly, Lz; 41 | double dx, dy, dz; 42 | int Nx, Ny, Nz; 43 | int num_cell_x, num_cell_y, num_cell_z, num_elements; //num_elements = (num_cell-1)^3 44 | std::string GenRateFileName; 45 | double Va_min, Va_max, increment; 46 | 47 | }; 48 | 49 | #endif // PARAMETERS_H 50 | -------------------------------------------------------------------------------- /3D/C++_implementation/Single-charge-carrier/parameters.inp: -------------------------------------------------------------------------------- 1 | //NOTE:IF-HAVE-ANY-SPACES-IN-COMMENTS-IT-WILL-FAIL 2 | 600.01e-9 //device-lenght(m)X 3 | 600.01e-9 //device-WIDTH(m)Y 4 | 80.01e-9 //device-thickness(m)Z 5 | 5.0e-9 //dx_mesh_size 6 | 5.0e-9 //dy_mesh_size 7 | 2.0e-9 //dz_mesh_size 8 | 1.25e27 //N-LUMO 9 | 1.25e27 //N-HOMO 10 | 3.0 //eps_active 11 | 1e-8 //p_mob_active 12 | 1e-8 //mobil 13 | 14 | 0.1 //Va_min 15 | 1.0 //Va_max 16 | 0.1 //increment 17 | 0.2 //w_eq 18 | 0.2 //w_i 19 | 1e-9 //tolerance_i 20 | 2.0 //w_reduce_factor 21 | 10.0 //tol_relax_factor 22 | 23 | -------------------------------------------------------------------------------- /3D/C++_implementation/Two-charge-carriers/3D_DD_Cpp.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | CONFIG += console c++14 3 | CONFIG -= app_bundle 4 | CONFIG -= qt 5 | 6 | QMAKE_CXXFLAGS += -openmp 7 | 8 | INCLUDEPATH += C:/Eigen 9 | DEPENDPATH += C:/Eigen 10 | 11 | SOURCES += main.cpp \ 12 | continuity_n.cpp \ 13 | continuity_p.cpp \ 14 | parameters.cpp \ 15 | photogeneration.cpp \ 16 | poisson.cpp \ 17 | recombination.cpp \ 18 | Utilities.cpp 19 | 20 | HEADERS += \ 21 | constants.h \ 22 | continuity_n.h \ 23 | continuity_p.h \ 24 | parameters.h \ 25 | photogeneration.h \ 26 | poisson.h \ 27 | recombination.h \ 28 | Utilities.h 29 | -------------------------------------------------------------------------------- /3D/C++_implementation/Two-charge-carriers/README.md: -------------------------------------------------------------------------------- 1 | # Drift-Diffusion_models 2 | 3 | 3D Drift Diffusion 4 | 5 | This solves the semiconductor drift-diffusion equations in 3D for both electrons and holes using finite differences. The equations are Poisson eqn, 6 | continuity equation, and drift-diffusion equation which are solved in a decoupled iterative method (Gummel method). Scharfetter-Gummel 7 | discretization as well as linear mixing of old and new solutions is used to maintain stability. 8 | 9 | The gen_rate.txt input file is needed for photogeneration.cpp, or can comment that section out and use an analytic expression or set it to zero if are not 10 | studying a device under illumination. The gen_rate.txt file should contain just 1 column of generation rates corresponding to each mesh point in the device (except the end points, x = 0 and x=L). An example generation rate file is included. 11 | 12 | The code was originally developed for solar cells, but can be used for any semiconductor devices. 13 | 14 | Code can be compiled using the makefile or the QT Creator .pro project file (just open that and run within QT). 15 | 16 | 17 | -------------------------------------------------------------------------------- /3D/C++_implementation/Two-charge-carriers/Utilities.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILITIES_H 2 | #define UTILITIES_H 3 | 4 | #include 5 | #include "parameters.h" 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | class Utilities 14 | { 15 | public: 16 | Utilities(); 17 | 18 | //!Applies linear mixing (result = w*new_value + (1-w)*old_value) where w is the mixing factor. 19 | //! The mixing factor is in the \param params object. 20 | std::vector linear_mix(const Parameters ¶ms,const std::vector &new_values,const std::vector &old_values); 21 | 22 | //!This writes to output files the details of voltage \param V, carrier densities \param p and \param n, current \param J_total, net electron generation rate \param Un. 23 | //! The files are named according to the applied voltage \param Va of this data. 24 | void write_details(const Parameters ¶ms, double Va, const Eigen::Tensor &V_matrix, const std::vector &p, const std::vector &n, const Eigen::Tensor &J_total_Z, const std::vector &Un); 25 | 26 | //!Writes to a file the JV data. The file contains 3 columns, the applied voltage \param Va, the current \param J_total, 27 | //! and the number of iterations \param iter required to converge at that voltage. 28 | void write_JV(const Parameters ¶ms, std::ofstream &JV, double iter, double Va, const Eigen::Tensor &J_total_Z); 29 | 30 | }; 31 | 32 | #endif // UTILITIES_H 33 | -------------------------------------------------------------------------------- /3D/C++_implementation/Two-charge-carriers/constants.h: -------------------------------------------------------------------------------- 1 | #ifndef CONSTANTS_H 2 | #define CONSTANTS_H 3 | 4 | #include 5 | 6 | //Physical Constants (these should not be changed) (constexpr b/c known at compile-time) 7 | constexpr double q = 1.60217646e-19; //elementary charge, C 8 | constexpr double kb = 1.3806503e-23; //Boltzmann const., J/k 9 | constexpr double T = 296.; //temperature K 10 | constexpr double Vt = (kb*T)/q; //thermal voltage V 11 | constexpr double epsilon_0 = 8.85418782e-12; //F/m 12 | 13 | 14 | #endif // CONSTANTS_H 15 | -------------------------------------------------------------------------------- /3D/C++_implementation/Two-charge-carriers/gen_rate.inp: -------------------------------------------------------------------------------- 1 | 1.83560545e+22 2 | 1.82755381e+22 3 | 1.81954608e+22 4 | 1.81158192e+22 5 | 1.80366097e+22 6 | 1.79578290e+22 7 | 1.78794737e+22 8 | 1.78015404e+22 9 | 1.77240259e+22 10 | 1.76469268e+22 11 | 12 | -------------------------------------------------------------------------------- /3D/C++_implementation/Two-charge-carriers/parameters.h: -------------------------------------------------------------------------------- 1 | #ifndef PARAMETERS_H 2 | #define PARAMETERS_H 3 | 4 | #include "constants.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | struct Parameters //parameters need to be accessble, so all members are public. 13 | { 14 | Parameters(){} 15 | void reduce_w(){w = w/w_reduce_factor;} 16 | void relax_tolerance(){tolerance = tolerance*tol_relax_factor;} 17 | void use_tolerance_eq() {tolerance = tolerance_eq;} 18 | void use_tolerance_i() {tolerance = tolerance_i;} 19 | void use_w_i() {w = w_i;} 20 | void use_w_eq() {w = w_eq;} 21 | 22 | void Initialize(); 23 | void isPositive(double input, const std::string &comment); 24 | void isPositive(int input, const std::string &comment); 25 | void isNegative(double input, const std::string &comment); 26 | void isNegative(int input, const std::string &comment); 27 | 28 | double N_LUMO, N_HOMO, phi_a, phi_c, eps_active, p_mob_active, n_mob_active; 29 | double dx, mobil; 30 | double E_gap, active_CB, active_VB, WF_anode, WF_cathode, N_dos, Nsqrd; 31 | double Photogen_scaling, k_rec; 32 | 33 | double w; 34 | double w_reduce_factor; 35 | double tolerance, tolerance_eq; 36 | double tol_relax_factor; 37 | double Vmin, Vmax; 38 | 39 | double tolerance_i, w_i, w_eq; 40 | double L; 41 | int num_cell, num_elements; //num_elements = (num_cell-1)^3 42 | std::string GenRateFileName; 43 | double Va_min, Va_max, increment; 44 | double Vbi; 45 | 46 | 47 | }; 48 | 49 | #endif // PARAMETERS_H 50 | -------------------------------------------------------------------------------- /3D/C++_implementation/Two-charge-carriers/parameters.inp: -------------------------------------------------------------------------------- 1 | //NOTE:IF-HAVE-ANY-SPACES-IN-COMMENTS-IT-WILL-FAIL 2 | 20.0e-9 //device-thickness(m) 3 | 20 //num_cell 4 | 1e24 //N-LUMO 5 | 1e24 //N-HOMO 6 | 4e27 //Photogeneration-scaling 7 | 0.2 //phi_a 8 | 0.1 //phi_c 9 | 3.0 //eps_active 10 | 4.5e-6 //p_mob_active 11 | 4.5e-6 //n_mob_active 12 | 5e-6 //mobil 13 | 1.5 //E_gap 14 | -3.9 //active_CB 15 | -5.4 //active_VB 16 | 4.8 //WF_anode 17 | 3.7 //WF_cathode 18 | 6e-17 //k_rec 19 | 1.0e-9 //dx 20 | 21 | -0.5 //Va_min 22 | -0.49 //Va_max 23 | 0.01 //increment 24 | 0.2 //w_eq 25 | 0.2 //w_i 26 | 5e-12 //tolerance_i 27 | 2.0 //w_reduce_factor 28 | 10.0 //tol_relax_factor 29 | gen_rate.inp //GenRateFileName 30 | 31 | -------------------------------------------------------------------------------- /3D/C++_implementation/Two-charge-carriers/photogeneration.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "photogeneration.h" 5 | 6 | //constructor definition 7 | Photogeneration::Photogeneration(const Parameters ¶ms, double photogen_scaling, const std::string gen_rate_file_name){ 8 | 9 | PhotogenRate.resize(params.num_cell, params.num_cell); //need 0 through N indices, and N = num_cell-1 10 | PhotogenRate_max = photogen_scaling; 11 | 12 | std::vector Photogen_vector(params.num_cell); //temporary vector, to input gen rate from file 13 | 14 | std::ifstream GenRateFile; 15 | 16 | GenRateFile.open(gen_rate_file_name); 17 | //check if file was opened 18 | if (!GenRateFile) { 19 | std::cerr << "Unable to open file gen_rate.txt"; 20 | exit(1); // call system to stop 21 | } 22 | 23 | for (int i = 1; i <= params.num_cell-1; i++) { 24 | GenRateFile >> Photogen_vector[i]; 25 | } 26 | 27 | //----normalize the photogen rate-------------------------- 28 | 29 | double maxOfGPhotogenRate = *std::max_element(Photogen_vector.begin(),Photogen_vector.end()); 30 | 31 | for (int i= 1; i <= params.num_cell-1; i++) { 32 | Photogen_vector[i] = PhotogenRate_max*Photogen_vector[i]/maxOfGPhotogenRate; 33 | //std::cout << "G(i) " << G[i] < 5 | #include "constants.h" 6 | #include "parameters.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | class Photogeneration 15 | { 16 | public: 17 | 18 | //!Constructor will get the generation rate from file. 19 | //!Generation rate file should contain num_cell -2 number of entries in a single column, corresponding to 20 | //!the the generation rate at each mesh point (except the endpoints). 21 | //! \param photogen_scaling is the scaling factor obtained from fit to get the correct short-circuit current. 22 | Photogeneration(const Parameters ¶ms, double photogen_scaling, const std::string gen_rate_file_name); 23 | 24 | Eigen::MatrixXd getPhotogenRate() {return PhotogenRate;} 25 | 26 | private: 27 | Eigen::MatrixXd PhotogenRate; 28 | double PhotogenRate_max; 29 | }; 30 | 31 | 32 | 33 | #endif // PHOTOGENERATION_H 34 | -------------------------------------------------------------------------------- /3D/C++_implementation/Two-charge-carriers/recombination.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "recombination.h" 4 | 5 | Recombo:: Recombo(const Parameters ¶ms) //constructor 6 | { 7 | k_rec = params.k_rec; 8 | R_Langevin.resize(params.num_cell); 9 | E_trap = params.active_VB + params.E_gap/2.0; //trap assisted recombo is most effective when trap is located mid-gap--> take VB and add 1/2 of bandgap 10 | n1 = params.N_LUMO*exp(-(params.active_CB - E_trap)/Vt); 11 | p1 = params.N_HOMO*exp(-(E_trap - params.active_VB)/Vt); 12 | } 13 | 14 | std::vector Recombo::ComputeR_Langevin(const Parameters ¶ms, const std::vector &n, const std::vector &p) 15 | { 16 | for (int i = 1; i < p.size(); i++) { 17 | R_Langevin[i] = k_rec*(params.N_dos*params.N_dos*n[i]*p[i] - n1*p1); 18 | if (R_Langevin[i] < 0.0) R_Langevin[i] = 0.0; //negative recombo is unphysical 19 | } 20 | 21 | //for now just use n and p vectors.... 22 | 23 | return R_Langevin; 24 | } 25 | 26 | -------------------------------------------------------------------------------- /3D/C++_implementation/Two-charge-carriers/recombination.h: -------------------------------------------------------------------------------- 1 | #ifndef RECOMBINATION_H 2 | #define RECOMBINATION_H 3 | 4 | #include 5 | #include "constants.h" 6 | #include "parameters.h" 7 | 8 | class Recombo{ 9 | public: 10 | //!Constructor sets up the recombination member parameters using the input from \param params 11 | Recombo(const Parameters ¶ms); 12 | 13 | //!Computes bimolecular Langevin recombination rate. 14 | //! This depends on the electron \param n and hole \param p density. 15 | std::vector ComputeR_Langevin(const Parameters ¶ms, const std::vector &n, const std::vector &p); 16 | 17 | private: 18 | double k_rec; //!bimolecular recombination coefficient 19 | double E_trap; //!Trap level energy 20 | double n1; 21 | double p1; 22 | std::vector R_Langevin; //!store the Langevin recombination rate 23 | }; 24 | 25 | 26 | #endif // RECOMBINATION_H 27 | -------------------------------------------------------------------------------- /3D/Matlab_implementation/Single-charge-carrier/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | *.smod 19 | 20 | # Compiled Static libraries 21 | *.lai 22 | *.la 23 | *.a 24 | *.lib 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | 31 | #data output 32 | *.txt 33 | 34 | #QT builds 35 | *.stash 36 | *.pdb 37 | *.Debug 38 | *.Release 39 | Makefile 40 | *.ilk 41 | *.pro 42 | *.user 43 | 44 | #results files 45 | *.jpg 46 | *.fig 47 | *.pptx 48 | *.docx 49 | *.asv 50 | *.pdf 51 | *.tex 52 | *.tif 53 | -------------------------------------------------------------------------------- /3D/Matlab_implementation/Single-charge-carrier/SetbV_3D.m: -------------------------------------------------------------------------------- 1 | function bV = SetbV_3D(p, epsilon) 2 | global V_bottomBC V_topBC Nx Ny Nz CV; 3 | 4 | %set up rhs of Poisson equation. Note for epsilons, are assuming that 5 | %epsilons at the boundaries are the same as espilon cell into interior of 6 | %device 7 | 8 | bV = CV*(p)./18.; %try scaling.... %note: this are in vector form 9 | 10 | index = 0; 11 | for i = 1:Nx+1 12 | for j = 1:Ny+1 13 | 14 | for k = 1:Nz+1 15 | index = index + 1; 16 | if (k == 1) %bottom BC 17 | bV(index) = bV(index) + (epsilon(i+1,j+1,0+1)./18.)*V_bottomBC(i,j); 18 | end 19 | %middle elements have no BC's 20 | if (k == Nz+1) %top BC\\ 21 | 22 | %1st change the charge part everywhere 23 | 24 | % bV(index) = bV(index)/2; %this is due to I used -epsilon_Z instead of -2*epsilon_Z in Poisson for neuman BC 25 | %NOT SURE IF THIS IS BETTER THAN JUST USING 26 | %-2*epsilon_Z, in the matrix, but this way we preserve 27 | %symmetry 28 | 29 | 30 | %IN THIS VERSION WE ALWAYS HAVE DIRICHLET BC'S for top 31 | %electrode--> just include that here.... 32 | %for Dirichet BC's, rhs should just = V_topBC --> we just 33 | %reset the bV(index) for these 34 | % if ( (i == floor((N+1)/2) || (i == floor((N+1)/2) + 1)) && (j == floor((N+1)/2) || (j == floor((N+1)/2) + 1))) 35 | 36 | bV(index) = V_topBC(i,j); %note: this corresponds to BC eqn which just specifies the Dirichlet BC 37 | %NOTE: use the V_topBC matrix to specify which i,j have a BC and which are 0 --> i.e. NOT really 0, but corresponds to Neumann BC's 38 | %NOTE: V_topBC is non-zero only for the i,j 39 | %corresponding to the tip--> Dirichlet BC's 40 | 41 | %if not in tip region, are in insulating region, and no BC 42 | %needs to be applied from rhs, b/c already taken care of in 43 | %the matrix. 44 | % end 45 | 46 | end 47 | end 48 | end 49 | end 50 | 51 | 52 | %Notice how much simpler rhs is with this ordering 53 | %and including PBC's in the matrix! -------------------------------------------------------------------------------- /3D/Matlab_implementation/Single-charge-carrier/Setbp_3D.m: -------------------------------------------------------------------------------- 1 | function bp = Setbp_3D(Bernoulli_p_values, p_mob, Up) 2 | global Cp num_elements p_topBC p_bottomBC Nx Ny Nz; 3 | 4 | % bp = zeros(num_elements,1); 5 | %Note: assuming for BC's that mobility on bndry is same as mobility just 6 | %inside the boundary (so not doing any mobs averaging there) 7 | 8 | %extract variables from struct (for brevity in eqns) 9 | % Bp_posX = Bernoulli_p_values.Bp_posX; 10 | % Bp_negX = Bernoulli_p_values.Bp_negX; 11 | % Bp_posY = Bernoulli_p_values.Bp_posY; 12 | % Bp_negY = Bernoulli_p_values.Bp_negY; 13 | Bp_posZ = Bernoulli_p_values.Bp_posZ; 14 | Bp_negZ = Bernoulli_p_values.Bp_negZ; 15 | 16 | %calculate main part here 17 | bp = Cp*Up; 18 | 19 | %add on BC's 20 | index = 0; 21 | for i = 1:Nx+1 22 | for j = 1:Ny+1 23 | for k = 1:Nz+1 24 | index = index + 1; 25 | if (k == 1) %bottom BC 26 | bp(index) = bp(index) + p_mob(i+1,j+1,0+1)*p_bottomBC(i,j)*Bp_posZ(i+1,j+1,k+1); 27 | end 28 | %middle elements have no BC's 29 | if (k == Nz+1) %top BC 30 | 31 | % bp(index) = bp(index)/2; %this is due to I used -p_mob_Z_avg.*Bp_posZ instead of -2*p_mob_Z_avg.*Bp_posZ in Poisson for neuman BC 32 | %NOT SURE IF THIS IS BETTER THAN JUST USING 33 | %-2*p_mob_Z_avg.*Bp_posZ, in the matrix, ESPECIALLY 34 | %SINCE Ap matrix is not symmetry already--> doing this 35 | %doessn't preserve symmetry.... 36 | 37 | 38 | %IN THIS VERSION WE ALWAYS HAVE DIRICHLET BC'S for top 39 | %electrode--> just include that here.... 40 | % if ( (i == floor((N+1)/2) || (i == floor((N+1)/2) + 1)) && (j == floor((N+1)/2) || (j == floor((N+1)/2) + 1))) 41 | bp(index) = p_topBC(i,j); %note: this corresponds to BC eqn which just specifies the Dirichlet BC 42 | 43 | %NOTE: p_topBC is non-zero only for the i,j 44 | %corresponding to the tip--> Dirichlet BC's 45 | 46 | %if not in tip region, are in insulating region, and no BC 47 | %needs to be applied from rhs, b/c already taken care of in 48 | %the matrix. 49 | % end 50 | end 51 | 52 | 53 | end 54 | end 55 | end 56 | end -------------------------------------------------------------------------------- /3D/Matlab_implementation/Two-charge-carriers/insulating_BCs_X_Y/GenerationRate.m: -------------------------------------------------------------------------------- 1 | function G = GenerationRate() 2 | 3 | global G_max num_elements 4 | 5 | % G = ones(N,N); 6 | G = ones(num_elements,1); %better to use it as a 1D vector 7 | 8 | %Here can use any relevant analytic expression or input data from a file 9 | % i.e. 10 | %G(2:num_cell) = load('gen_rate.txt'); 11 | %G(x,z) 12 | % G(1:N,1:N) = 1; %these just include the insides--> since want Un and Up to just include the insides--> i,j directly correspond to the matrix indices 13 | 14 | G = G_max.*G; %skip the endpoints b/c we don't need them in Un... 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /3D/Matlab_implementation/Two-charge-carriers/periodic_BCs_X_Y/SetbV_3D.asv: -------------------------------------------------------------------------------- 1 | function bV = SetbV_3D(p, epsilon) 2 | global V_leftBC_x V_leftBC_y V_bottomBC V_topBC V_rightBC_x V_rightBC_y N CV; 3 | 4 | %set up rhs of Poisson equation. Note for epsilons, are assuming that 5 | %epsilons at the boundaries are the same as espilon cell into interior of 6 | %device 7 | 8 | bV = CV*(p); %note: this are in vector form 9 | 10 | index = 0; 11 | for i = 1:N+1 12 | for j = 1:N+1 13 | for k = 1:N 14 | if(k == 1) %bottom BC 15 | -------------------------------------------------------------------------------- /3D/Matlab_implementation/Two-charge-carriers/periodic_BCs_X_Y/SetbV_3D.m: -------------------------------------------------------------------------------- 1 | function bV = SetbV_3D(p, epsilon) 2 | global V_bottomBC V_topBC N CV; 3 | 4 | %set up rhs of Poisson equation. Note for epsilons, are assuming that 5 | %epsilons at the boundaries are the same as espilon cell into interior of 6 | %device 7 | 8 | bV = CV*(p); %note: this are in vector form 9 | 10 | index = 0; 11 | for i = 1:N+1 12 | for j = 1:N+1 13 | for k = 1:N 14 | index = index + 1; 15 | if (k == 1) %bottom BC 16 | bV(index) = bV(index) + epsilon(i+1,j+1,0+1)*V_bottomBC(i,j); 17 | end 18 | %middle elements have no BC's 19 | if (k == N) %top BC 20 | bV(index) = bV(index) + epsilon(i+1,j+1,N+1)*V_topBC(i,j); 21 | end 22 | end 23 | end 24 | end 25 | 26 | 27 | %Notice how much simpler rhs is with this ordering 28 | %and including PBC's in the matrix! -------------------------------------------------------------------------------- /3D/Matlab_implementation/Two-charge-carriers/periodic_BCs_X_Y/Setbp_3D.m: -------------------------------------------------------------------------------- 1 | function bp = Setbp_3D(Bernoulli_p_values, p_mob, Up) 2 | global Cp num_elements p_topBC p_bottomBC N; 3 | 4 | % bp = zeros(num_elements,1); 5 | %Note: assuming for BC's that mobility on bndry is same as mobility just 6 | %inside the boundary (so not doing any mobs averaging there) 7 | 8 | %extract variables from struct (for brevity in eqns) 9 | Bp_posX = Bernoulli_p_values.Bp_posX; 10 | Bp_negX = Bernoulli_p_values.Bp_negX; 11 | Bp_posY = Bernoulli_p_values.Bp_posY; 12 | Bp_negY = Bernoulli_p_values.Bp_negY; 13 | Bp_posZ = Bernoulli_p_values.Bp_posZ; 14 | Bp_negZ = Bernoulli_p_values.Bp_negZ; 15 | 16 | %calculate main part here 17 | bp = Cp*Up; 18 | 19 | %add on BC's 20 | index = 0; 21 | for i = 1:N+1 22 | for j = 1:N+1 23 | for k = 1:N 24 | index = index + 1; 25 | if (k == 1) %bottom BC 26 | bp(index) = bp(index) + p_mob(i+1,j+1,0+1)*p_bottomBC(i,j)*Bp_posZ(i+1,j+1,k+1); 27 | end 28 | %middle elements have no BC's 29 | if (k == N) %top BC 30 | bp(index) = bp(index) + p_mob(i+1,j+1,N+1)*p_topBC(i,j)*Bp_negZ(i+1,j+1,k+1+1); 31 | end 32 | end 33 | end 34 | end -------------------------------------------------------------------------------- /3D/README.md: -------------------------------------------------------------------------------- 1 | # Drift-Diffusion_models 2 | 3 | 3D Drift Diffusion 4 | 5 | This contains Matlab and C++ implementations for solving the semiconductor drift-diffusion equations in 3D using finite differences. The "Two-charge-carriers" versions of the models currently solve for a solar cell under illumination. The "Single-charge-carrier" versions solve for the current-voltage curve of a material which only has holes as the free carrier and is under a varying applied voltage in the dark. All of the models can be modified to solve other systems (i.e. through changing the boundary conditions, adding recombination rates, and modifying the generation rate). 6 | 7 | The equations are Poisson eqn, 8 | continuity equation, and drift-diffusion equation which are solved in a decoupled iterative method (Gummel method). Scharfetter-Gummel 9 | discretization as well as linear mixing of old and new solutions is used to maintain stability. 10 | 11 | 12 | Periodic boundary conditions are used for the x and y boundaries and Dirichlet boundary conditions are used for the z boundaries in order to be able to apply a voltage in the z direction. The electrodes of the device are assumed to be located at z = 0 and z = Lz. 13 | 14 | -------------------------------------------------------------------------------------------------------------- 15 | Requirements for C++ implementations: 16 | In addition to a C++11 compiler, the linear algebra library Eigen is needed. The library is 17 | composed of only header files, so no linking is necessary. One just needs to specify the directory of the library files in the include. Eigen can be downloaded at: http://eigen.tuxfamily.org/index.php?title=Main_Page 18 | 19 | Also the input files: "parameters.inp" and "gen_rate.inp" (for the 2 carrier versions) need to be located in the same directory as the source code. 20 | 21 | Also include the openmp compiler flag to allow for Eigen to parallelize the matrix solving. 22 | 23 | ------------------------------------------------ 24 | The Matlab implementations only require Matlab. 25 | 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Timofey Golubev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. --------------------------------------------------------------------------------