From 709b087c4b1683f923e4603a6fa0980318e4c96e Mon Sep 17 00:00:00 2001
From: Phoebe Pearce
Date: Thu, 19 Oct 2023 09:46:32 +1100
Subject: [PATCH] add sesame to requirements, add example. QE and IV working
---
examples/sesame_PDD_example.py | 77 +++++++++++++
examples/sesame_testing.py | 3 +-
pyproject.toml | 1 +
solcore/sesame_drift_diffusion/__init__.py | 1 +
solcore/sesame_drift_diffusion/solve_pdd.py | 114 +++++++++-----------
solcore/solar_cell_solver.py | 4 +-
6 files changed, 134 insertions(+), 66 deletions(-)
create mode 100644 examples/sesame_PDD_example.py
diff --git a/examples/sesame_PDD_example.py b/examples/sesame_PDD_example.py
new file mode 100644
index 00000000..60cb4dac
--- /dev/null
+++ b/examples/sesame_PDD_example.py
@@ -0,0 +1,77 @@
+from solcore import material, si
+from solcore.solar_cell import SolarCell, Junction, Layer
+from solcore.state import State
+from solcore.solar_cell_solver import solar_cell_solver
+from solcore.light_source import LightSource
+import numpy as np
+import matplotlib.pyplot as plt
+
+options = State()
+options.wavelength = np.linspace(280, 700, 20)*1e-9
+options.optics_method = 'TMM'
+options.light_iv = True
+options.light_source = LightSource(source_type="standard",
+ version="AM1.5g", x=options.wavelength, output_units="photon_flux_per_m")
+options.voltages = np.linspace(0, 2, 40)
+
+T = 293
+
+add_args = {'relative_permittivity': 10, 'electron_minority_lifetime': 5e-6,
+ 'hole_minority_lifetime': 5e-6,
+ 'electron_auger_recombination': 1e-45,
+ 'hole_auger_recombination': 1e-45}
+
+ARC = material('Si3N4')()
+window = material('AlGaAs')(T=T, Na=5e24, Al=0.8, **add_args)
+p_AlGaAs = material('AlGaAs')(T=T, Na=1e24, Al=0.4, **add_args)
+n_AlGaAs = material('AlGaAs')(T=T, Nd=8e22, Al=0.4, **add_args)
+bsf = material('AlGaAs')(T=T, Nd=2e24, Al=0.6, **add_args)
+
+junction = Junction([Layer(width=si('30nm'), material=window, role="Window"),
+ Layer(width=si('150nm'), material=p_AlGaAs, role="Emitter"),
+ Layer(width=si('1000nm'), material=n_AlGaAs, role="Base"),
+ Layer(width=si('200nm'), material=bsf, role="BSF")], sn=1e6, sp=1e6, T=T, kind='sesame_PDD')
+
+widths = [layer.width for layer in junction]
+junction.mesh = np.linspace(0, np.sum(widths), 1000)
+
+solar_cell = SolarCell(
+ [Layer(60e-0, ARC), junction]
+)
+
+# solar_cell_solver(solar_cell, 'iv', options)
+solar_cell_solver(solar_cell, 'qe', options)
+
+# # qe_sesame(solar_cell[1], options)
+#
+# iv_sesame(solar_cell[1], options)
+
+junction_old = Junction([Layer(width=si('30nm'), material=window, role="Window"),
+ Layer(width=si('150nm'), material=p_AlGaAs, role="Emitter"),
+ Layer(width=si('1000nm'), material=n_AlGaAs, role="Base"),
+ Layer(width=si('200nm'), material=bsf, role="BSF")], sn=1e6, sp=1e6, T=T,
+ kind='PDD')
+
+
+solar_cell_old = SolarCell(
+ [Layer(60e-0, ARC), junction_old]
+)
+
+# solar_cell_solver(solar_cell_old, 'iv', options)
+#
+# plt.figure()
+# plt.plot(options.voltages, solar_cell[1].iv(options.voltages), 'o-', label='IV')
+# plt.plot(options.voltages, solar_cell_old[1].iv(options.voltages), 'o-', label='IV')
+# # plt.xlim(-2, 1.8)
+# plt.ylim(-400, 400)
+# plt.show()
+
+plt.figure()
+plt.plot(options.wavelength*1e9, solar_cell.absorbed)
+# plt.plot(options.wavelength*1e9, solar_cell_old.absorbed)
+plt.show()
+
+plt.figure()
+plt.plot(options.wavelength*1e9, solar_cell[1].eqe(options.wavelength))
+plt.plot(options.wavelength*1e9, solar_cell[1].iqe(options.wavelength))
+plt.show()
\ No newline at end of file
diff --git a/examples/sesame_testing.py b/examples/sesame_testing.py
index 691baffe..037bfd81 100644
--- a/examples/sesame_testing.py
+++ b/examples/sesame_testing.py
@@ -214,7 +214,8 @@ def gcfn(x, y):
flux = 1e20
print(wl)
sys.generation(gfcn_fun_rt(i1, flux))
-
+ print(gfcn_fun_rt(i1, flux)(0, 0))
+ print(gfcn_fun_rt(i1, flux)(1e-6, 0))
voltages = [0]
if i1 == 0:
diff --git a/pyproject.toml b/pyproject.toml
index 705a2e70..7d646f3d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -39,6 +39,7 @@ dependencies = [
"pyyaml",
"yabox",
"joblib",
+ "sesame@git+https://github.com/phoebe-p/sesame.git"
]
dynamic = ["version"]
diff --git a/solcore/sesame_drift_diffusion/__init__.py b/solcore/sesame_drift_diffusion/__init__.py
index e69de29b..61c2cc9b 100644
--- a/solcore/sesame_drift_diffusion/__init__.py
+++ b/solcore/sesame_drift_diffusion/__init__.py
@@ -0,0 +1 @@
+from .solve_pdd import qe_sesame, iv_sesame, equilibrium
\ No newline at end of file
diff --git a/solcore/sesame_drift_diffusion/solve_pdd.py b/solcore/sesame_drift_diffusion/solve_pdd.py
index 892e4110..9f481c38 100644
--- a/solcore/sesame_drift_diffusion/solve_pdd.py
+++ b/solcore/sesame_drift_diffusion/solve_pdd.py
@@ -2,7 +2,7 @@
import numpy as np
from solcore.constants import q
from solcore.light_source import LightSource
-from solcore.interpolate import interp1d
+from scipy.interpolate import interp1d
from solcore.state import State
from solcore.registries import (
@@ -148,7 +148,9 @@ def equilibrium():
@register_iv_solver("sesame_PDD", reason_to_exclude=reason_to_exclude)
def iv_sesame(junction, options):
+ print('hello')
+ # TODO: inclusion of shunt resistance
if ~hasattr(junction, "sesame_sys"):
process_structure(junction)
@@ -174,24 +176,37 @@ def iv_sesame(junction, options):
voltages = options.voltages
j, result = sesame.IVcurve(junction.sesame_sys, voltages) # , verbose=False)
- j = j * junction.sesame_sys.scaling.current
+ j = j * junction.sesame_sys.scaling.current * 1e4 # cm-2 -> m-2
+ print(result['v'])
- Jsc = j[0]*1e4 # units?
+ # Jsc = j[0] # units?
+ junction.current = j
- # jsc = q*np.trapz(eqe*light_source.spectrum()[1], wls) # A/m2
-
- zero_crossing = np.where(np.diff(np.sign(j)))[0][0]
- j_above = j[zero_crossing]
- j_below = j[zero_crossing + 1]
-
- Voc = voltages[zero_crossing] + (voltages[zero_crossing + 1] - voltages[zero_crossing]) * j_above / (
- j_above - j_below)
+ junction.iv = interp1d(
+ voltages,
+ j,
+ kind="linear",
+ bounds_error=False,
+ assume_sorted=True,
+ fill_value=(junction.current[0], junction.current[-1]),
+ )
- voltages_for_mpp = voltages * (np.abs(voltages) <= Voc)
- Vmpp = voltages[np.nanargmax(np.abs(j * voltages_for_mpp))]
- Jmpp = j[np.nanargmax(np.abs(j * voltages_for_mpp))] * 1e4
+ junction.voltage = voltages
+ # jsc = q*np.trapz(eqe*light_source.spectrum()[1], wls) # A/m2
- FF = Vmpp * Jmpp / (Jsc * Voc)
+ # this is all done externally, same for all junction types
+ # zero_crossing = np.where(np.diff(np.sign(j)))[0][0]
+ # j_above = j[zero_crossing]
+ # j_below = j[zero_crossing + 1]
+ #
+ # Voc = voltages[zero_crossing] + (voltages[zero_crossing + 1] - voltages[zero_crossing]) * j_above / (
+ # j_above - j_below)
+ #
+ # voltages_for_mpp = voltages * (np.abs(voltages) <= Voc)
+ # Vmpp = voltages[np.nanargmax(np.abs(j * voltages_for_mpp))]
+ # Jmpp = j[np.nanargmax(np.abs(j * voltages_for_mpp))] * 1e4
+ #
+ # FF = Vmpp * Jmpp / (Jsc * Voc)
def qe_sesame(junction, options):
@@ -205,11 +220,26 @@ def qe_sesame(junction, options):
voltages = [0]
+ # profile_func = interp1d(bulk_positions_cm, 1e7 * Si_profile, kind='linear', bounds_error=False, fill_value=0)
+ profile_func = junction.absorbed
+
+ A = np.trapz(junction.absorbed, junction.mesh, axis=0)
+ print(A)
+ print(junction.layer_absorption)
+
+ def make_gfcn_fun(wl_index, flux):
+ def gcfn_fun(x, y):
+ return flux * profile_func(x/100)[wl_index]/100 # convert to cm-1 from m-1
+
+ return gcfn_fun
+
for i1, wl in enumerate(wls):
flux = 1e20
print(wl)
- junction.sesame_sys.generation(gfcn_fun(i1, flux))
+ junction.sesame_sys.generation(make_gfcn_fun(i1, flux))
+ print(make_gfcn_fun(i1, flux)(0, 0))
+ print(make_gfcn_fun(i1, flux)(1e-6, 0))
if i1 == 0:
guess = None
@@ -219,14 +249,14 @@ def qe_sesame(junction, options):
j, result = sesame.IVcurve(junction.sesame_sys, voltages, guess=guess)
- eqe[i1] = j / (q * flux)
+ eqe[i1] = np.abs(j) / (q * flux)
+ print('j, q, flux', j, q, flux)
- A = junction.layer_absorption
+ eqe = eqe * junction.sesame_sys.scaling.current
iqe = eqe / A
# convert dimensionless current to dimension-ful current
- eqe = eqe * junction.sesame_sys.scaling.current
- iqe = iqe * junction.sesame_sys.scaling.current
+ # iqe = iqe * junction.sesame_sys.scaling.current
junction.iqe = interp1d(wls, iqe)
@@ -249,47 +279,3 @@ def qe_sesame(junction, options):
-from solcore import material, si
-from solcore.solar_cell import SolarCell, Junction, Layer
-from solcore.state import State
-from solcore.solar_cell_solver import solar_cell_solver
-from solcore.light_source import LightSource
-
-options = State()
-options.wavelength = np.linspace(280, 600, 20)*1e-9
-options.optics_method = 'TMM'
-options.light_iv = True
-options.light_source = LightSource(source_type="standard",
- version="AM1.5g", x=options.wavelength, output_units="photon_flux_per_m")
-options.voltages = np.linspace(0, 2, 100)
-
-T = 293
-
-add_args = {'relative_permittivity': 10, 'electron_minority_lifetime': 5e-6,
- 'hole_minority_lifetime': 5e-6,
- 'electron_auger_recombination': 1e-45,
- 'hole_auger_recombination': 1e-45}
-
-ARC = material('Si3N4')()
-window = material('AlGaAs')(T=T, Na=5e24, Al=0.8, **add_args)
-p_AlGaAs = material('AlGaAs')(T=T, Na=1e24, Al=0.4, **add_args)
-n_AlGaAs = material('AlGaAs')(T=T, Nd=8e22, Al=0.4, **add_args)
-bsf = material('AlGaAs')(T=T, Nd=2e24, Al=0.6, **add_args)
-
-junction = Junction([Layer(width=si('30nm'), material=window, role="Window"),
- Layer(width=si('150nm'), material=p_AlGaAs, role="Emitter"),
- Layer(width=si('1000nm'), material=n_AlGaAs, role="Base"),
- Layer(width=si('200nm'), material=bsf, role="BSF")], sn=1e6, sp=1e6, T=T, kind='PDD')
-
-widths = [layer.width for layer in junction]
-junction.mesh = np.linspace(0, np.sum(widths), 1000)
-
-solar_cell = SolarCell(
- [Layer(60e-0, ARC), junction]
-)
-
-solar_cell_solver(solar_cell, 'optics', options)
-
-# qe_sesame(solar_cell[1], options)
-
-iv_sesame(solar_cell[1], options)
\ No newline at end of file
diff --git a/solcore/solar_cell_solver.py b/solcore/solar_cell_solver.py
index 1b1ba93f..4e8009d0 100755
--- a/solcore/solar_cell_solver.py
+++ b/solcore/solar_cell_solver.py
@@ -169,7 +169,7 @@ def solve_iv(solar_cell, options):
if solar_cell[j].kind == "PDD":
PDD.iv_pdd(solar_cell[j], **options)
elif solar_cell[j].kind == "sesame_PDD":
- sesame_PDD.iv_sesame(solar_cell[j], **options)
+ sesame_PDD.iv_sesame(solar_cell[j], options)
elif solar_cell[j].kind == "DA":
ASC.iv_depletion(solar_cell[j], options)
elif solar_cell[j].kind == "2D":
@@ -225,6 +225,8 @@ def solve_qe(solar_cell, options):
for j in solar_cell.junction_indices:
if solar_cell[j].kind == "PDD":
PDD.qe_pdd(solar_cell[j], options)
+ elif solar_cell[j].kind == "sesame_PDD":
+ sesame_PDD.qe_sesame(solar_cell[j], options)
elif solar_cell[j].kind == "DA":
ASC.qe_depletion(solar_cell[j], options)
elif solar_cell[j].kind == "2D":