# -*- coding: utf-8 -*-
import pandas as pd
import numpy as np
from scipy import constants
from scipy import interpolate as interp
[docs]def calc_current(spec: pd.DataFrame, eqe: pd.DataFrame) -> np.ndarray:
"""
Calculates the photocurrent from timeseries of impinging spectrum (W/nm/m²) and eqe.
Parameters
----------
spec : pandas.Dataframe
Time series of spectral irradiance in the plane of the solar cell. The
names columns of the DataFrame have to be the wavelength of the incidenting
light in nm.
eqe : pandas.Dataframe
External quantum efficiency of the solar cell.
Returns
-------
current : numpy.array
Current generated in the solar cell in A/m²
"""
if len(spec.shape) > 1:
# spec is timeseries of spectral illumination
norm_absorbtion = spec.multiply(eqe, axis=1)
wl_arr = norm_absorbtion.columns.to_series()
photon_flux = (norm_absorbtion / constants.h / constants.c).multiply(
wl_arr * 1e-9, axis=1
)
current = pd.Series(
np.trapz(photon_flux, x=wl_arr) * constants.e, index=spec.index
)
try:
current.rename(eqe.name, inplace=True)
except:
pass
else:
# spec is single spectrum
norm_absorbtion = eqe.multiply(spec, axis=0)
wl_arr = norm_absorbtion.index.to_series()
photon_flux = (norm_absorbtion / constants.h / constants.c).multiply(
wl_arr * 1e-9, axis=0
)
current = np.trapz(photon_flux, x=wl_arr, axis=0) * constants.e
return current
# return photo_flux.apply(np.trapz, x=wl_arr, axis=1) * constants.e
[docs]def calc_temp_from_NOCT(noct, ambient_temp, irrad_poa):
"""
Calculates the cell temperature based on the irradiance in the plane of array,
ambient temperature and NOCT (nominal operating cell temperature)
Parameters
----------
noct : numeric or array-like
NOCT (nominal operating cell temperature) of the cell
ambient_temp : numeric or array-like
DESCRIPTION.
irrad_poa : numeric or array-like
irradiance in the plane of the pv module
Returns
-------
cell_temp : numeric or array-like
operating temperaure of the cell
"""
cell_temp = ambient_temp + (noct - 20) / 800 * irrad_poa
return cell_temp
[docs]def interp_eqe_to_spec(eqe: pd.DataFrame, spec: pd.DataFrame) -> pd.DataFrame:
try:
f = interp.interp1d(eqe.index, eqe, axis=0)
new_eqe = f(spec.columns)
new_eqe = pd.DataFrame(
new_eqe, index=spec.columns, columns=eqe.columns
)
return new_eqe
except ValueError as v_error:
if str(v_error) == "object arrays are not supported":
raise ValueError(
"""It seems that either the wavelength of the spectral or eqe df
are not numeric. Try to convert them to a numeric type,
e.g.\ndf_spec.columns = df_spec.columns.astype(float)"""
)
else:
print("Failed raise")
raise v_error
[docs]def interp_spec_to_eqe(eqe: pd.DataFrame, spec: pd.DataFrame) -> pd.DataFrame:
try:
f = interp.interp1d(spec.columns, spec, axis=1)
new_spec = f(eqe.index)
new_spec = pd.DataFrame(new_spec, index=spec.index, columns=eqe.index)
return new_spec
except ValueError as v_error:
if str(v_error) == "object arrays are not supported":
print(
"""It seems that either the wavelength of the spectral or eqe df
are not numeric. Try to convert them to a numeric type,
e.g.\ndf_spec.columns = df_spec.columns.astype(float)"""
)
else:
raise v_error
[docs]def calc_j0_RT(
eqe, bandgap_shift=None, lqe_ele=0.01
): # use wl on the x-axis
"""
Function to calculate J0 for the detailed balance limit at room temperature.
E_bg: Bandgap of model materials
lqe_eqe: Electroluminescent emission efficiency. For Shockley–Queisser equals 1.
return: J0 (dark current) (mA/cm²)
"""
# E_ev = np.linspace(E_bg,10,1000)
# t = time.process_time()
bbr = lambda wl, T: np.divide(
(2 * constants.c / wl**4),
(np.exp(constants.h * constants.c / (wl * constants.k * T) - 1.0)),
) # original
if bandgap_shift is not None:
eqe_wl_array = np.multiply.outer(eqe.index, bandgap_shift)
else:
eqe_wl_array = eqe.index
product = eqe.values.flatten() * bbr(eqe_wl_array * 1e-9, 300).T
integral = np.trapz(y=product, x=eqe_wl_array.T * 1e-9)
j0 = (
integral * 2 * np.pi * constants.e / lqe_ele * 0.1
) # Factor 0.1 for conversion from A/m² to mA/cm²!
return j0
if __name__ == "__main__":
spec = pd.read_csv("./data/tiny_spec.csv", index_col=0)
spec.columns = spec.columns.astype(float)
eqe = pd.read_csv("./data/eqe_tandem_2t.csv", index_col=0)
eqe_new = interp_eqe_to_spec(eqe, spec)
eqe.plot()
new_spec = interp_spec_to_eqe(eqe.loc[:1200], spec)
new_spec.iloc[50].plot()
spec.iloc[50].plot()
j_ph = pd.concat(
[
calc_current(spec / 1000, eqe_new["pero"]),
calc_current(spec / 1000, eqe_new["si"]),
],
axis=1,
)
import matplotlib.dates as mdates
import matplotlib.pyplot as plt
ax = j_ph.plot()
ax.tick_params(axis="x", labelrotation=-45)
# myFmt = mdates.DateFormatter('%Y-%m') # here you can format your datetick labels as desired
# ax.xaxis.set_major_formatter(myFmt)
plt.show()