codexpast-exam-of-the-mphil-in-micro-and-nanotechnology-enterprise-course-of-the-university-of-cambridge2023paper-1-additional-solar-limit.py
past-exam-of-the-mphil-in-micro-and-nanotechnology-enterprise-course-of-the-university-of-cambridge/2023/paper-1-additional-solar-limit.py
"""Original radiative detailed-balance example, Python 3.14/numpy/matplotlib.
6000 K sun, 300 K cell, angular radius 0.00465 rad, one emitting hemisphere.
A step absorber and one collected electron per absorbed photon are assumed.
Output is a PNG basename in the current working directory.
"""
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import numpy as np
KB_EV=8.617333262145179e-5
SUN_T=6000.0
CELL_T=300.0
SUN_RADIUS=0.00465
def bose_tail(x):
"""Integral x..infinity of t^2/(exp(t)-1), using its convergent series."""
x=np.asarray(x,dtype=float)
n=np.arange(1,401,dtype=float).reshape((-1,)+(1,)*x.ndim)
return np.sum(np.exp(-n*x)*(x*x/n+2*x/n**2+2/n**3),axis=0)
def radiative_voltage(gap):
"""Common photon-radiance factors cancel from the photocurrent/dark ratio."""
gap=np.asarray(gap,dtype=float)
solar=np.pi*np.sin(SUN_RADIUS)**2*(KB_EV*SUN_T)**3*bose_tail(gap/(KB_EV*SUN_T))
dark=np.pi*(KB_EV*CELL_T)**3*bose_tail(gap/(KB_EV*CELL_T))
return KB_EV*CELL_T*np.log1p(solar/dark)
gaps=np.linspace(1.0,2.5,400)
voltage=radiative_voltage(gaps)
deficit=gaps-voltage
points=np.array([1.31,1.73,2.32])
pv=radiative_voltage(points)
fig,axes=plt.subplots(1,2,figsize=(10,4.6),layout='constrained')
fig.patch.set_facecolor('white')
ax=axes[0]
ax.plot(gaps,gaps,'--',color='#929ba3',label=r'$E_g/q$')
ax.plot(gaps,voltage,color='#2f758a',lw=2,label='Radiative open circuit')
ax.scatter(points,pv,c='#ad5936',zorder=3)
for x,y,label in zip(points,pv,['CsSnI$_3$','CsPbI$_3$','CsPbBr$_3$']):
ax.annotate(label,xy=(x,y),xytext=(x-.18,y-.18),fontsize=10,
arrowprops={'arrowstyle':'-','color':'#ad5936','lw':.7})
ax.set(xlabel='Band gap (eV)',ylabel='Voltage (V)',ylim=(.55,2.6),title='Declared blackbody / step-absorber model')
ax.legend(loc='upper left',fontsize=9)
ax=axes[1]
a=ax.plot(gaps,deficit,color='#2f758a',lw=2,label='Absolute deficit (V)')
ax.scatter(points,points-pv,c='#ad5936',zorder=3)
ax.set(xlabel='Band gap (eV)',ylabel=r'$E_g/q-V_{OC}$ (V)',ylim=(.23,.36),title='Absolute loss rises slowly; fractional loss falls')
right=ax.twinx()
b=right.plot(gaps,100*deficit/gaps,'--',color='#92568b',lw=1.8,label='Fraction of gap voltage (%)')
right.set(ylabel='Deficit / gap voltage (%)',ylim=(8,30))
ax.legend(a+b,[v.get_label() for v in a+b],loc='upper center',fontsize=8.5)
for ax in axes:
ax.set_xlim(1,2.5)
ax.grid(alpha=.18)
fig.savefig('paper-1-additional-solar-limit.png',dpi=160,facecolor='white')
plt.close(fig)