Source code for pv_tandem.utils

# -*- 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()