-
Notifications
You must be signed in to change notification settings - Fork 73
/
solar_cell.py
executable file
·158 lines (122 loc) · 6.33 KB
/
solar_cell.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
from solcore.structure import Layer, Junction, Structure, TunnelJunction
from solcore import material, si
import numpy as np
def default_GaAs(T):
# We create the other materials we need for the device
window = material('AlGaAs')(T=T, Na=5e24, Al=0.8)
p_GaAs = material('GaAs')(T=T, Na=1e24)
n_GaAs = material('GaAs')(T=T, Nd=8e22)
bsf = material('GaAs')(T=T, Nd=2e24)
output = Junction([Layer(width=si('30nm'), material=window, role="Window"),
Layer(width=si('150nm'), material=p_GaAs, role="Emitter"),
Layer(width=si('3000nm'), material=n_GaAs, role="Base"),
Layer(width=si('200nm'), material=bsf, role="BSF")], sn=1e6, sp=1e6, T=T, kind='PDD')
return output
class SolarCell(Structure):
""" This class is almost identical to the basic Structure class in Solcore (it is a subclass of it, actually) but implementing some default parameter values and control about the types of layers. It should work anywhere where a Structure object works.
"""
def __init__(self, layers=None, T=298, cell_area=1, reflectivity=None, shading=0, substrate=None,
R_series=0, **kwargs):
""" Constructor of the class.
:param layers: A list with the layers to add. The layers might include individual Layer objects of whole Junction objects.
:param T: Temperature.
:param cell_area: The area of the cell.
:param reflectivity: Function that calculates the reflectivity as a function of energy.
:param shading: Shading losses due to the front metal contacts.
:param substrate: Substrate of the solar cell.
:param R_series: Series resistance of the structure
:param kwargs: Other possible attributes.
"""
if layers is None:
layers = [default_GaAs(T)]
else:
assert isinstance(layers, list), "Layers must be provided inside a list, even if it is just one layer."
super(SolarCell, self).__init__(layers)
self.__dict__.update(kwargs)
self.T = T
self.cell_area = cell_area
self.shading = shading
self.reflectivity = reflectivity
self.junctions = 0
self.junction_indices = []
self.tunel_indices = []
self.substrate = substrate
self.R_series = R_series
for i, element in enumerate(layers):
self.sort_layer_type(element, i)
self.R_series += element.R_series if hasattr(element, 'R_series') else 0
def sort_layer_type(self, layer, i):
""" Sorts the layer in different categories, depending on its type, and keeps record on the indices of that type of layer.
:param layer: The layer to check
:param i: The index of that layer
:return: None
"""
if type(layer) == Junction:
self.junction_indices.append(i)
self.junctions += 1
if type(layer) == TunnelJunction:
self.tunel_indices.append(i)
def append(self, new_layer, layer_label=None, repeats=1):
""" Appends a layer to the structure a certain number of times to the structure.
:param new_layer: The layer to append.
:param layer_label: An optional label for that layer.
:param repeats: Number of times to repeat add the layer.
:return: None
"""
for i in range(repeats):
self.sort_layer_type(new_layer, len(self))
super(SolarCell, self).append(new_layer, layer_label)
def append_multiple(self, layers, layer_labels=None, repeats=1):
""" Appends multiple layers a certain umber of times to the structure.
:param layers: A list with the layers to append.
:param layer_labels: An optional list with the labels. If present, it must have the same length that the layers list.
:param repeats: Number of times to add this set of layers.
:return: None
"""
assert isinstance(layers, list), "'append_multiple' only accepts lists for the first argument."
if layer_labels is not None:
assert len(layers) == len(layer_labels), "When using 'layer_labels' keyword a label must be specified for " \
"each layer added i.e. layers and layer_labels must have the same " \
"number of elements. Either fix this or simply do not assign any " \
"labels (i.e. layer_labels=None)."
else:
layer_labels = [None] * len(layers)
for i in range(repeats):
for j, element in enumerate(layers):
self.append(element, layer_labels[j])
def update_junction(self, junction, **kwargs):
""" Adds or updates the attributes - not the layers - of a junction.
:param junction: The junction to update.
:param kwargs: The attributes to update.
:return: None
"""
try:
num = self.junction_indices[junction]
self[num].__dict__.update(kwargs)
except IndexError:
print('ERROR updating junction: The junction index must be {} or less.'.format(len(self.junction_indices)))
def __call__(self, i):
return self[self.junction_indices[i]]
if __name__ == '__main__':
window = material('AlGaAs')(T=298, Na=1e24, Al=0.8)
stack = [Layer(width=si("50nm"), material=window),
default_GaAs(298)]
# stack = [default_GaAs(298)]
my_cell = SolarCell(layers=stack)
#
# my_cell.append_multiple([default_GaAs(298), default_GaAs(298), default_GaAs(298)])
# print(my_cell)
from solcore.poisson_drift_diffusion.DriftDiffusionUtilities import solve_pdd, default_photon_flux, \
default_wavelengths
import matplotlib.pyplot as plt
solve_pdd(my_cell, 'QE', vfin=1.2, vstep=0.05, light=True)
QE = my_cell(0).qe
# Finally, we plot the internal and external quantum efficiencies using the information stored in the output dictionaries
plt.plot(QE['QE']['wavelengths'] / 1e-9, QE['QE']['IQE'] * 100, label='IQE')
plt.plot(QE['QE']['wavelengths'] / 1e-9, QE['QE']['EQE'] * 100, label='EQE')
plt.plot(QE['QE']['wavelengths'] / 1e-9, my_cell.T * 100, label='T')
plt.ylim(0, 100)
plt.legend(loc='lower left')
plt.ylabel('QE (%)')
plt.xlabel('Wavelength (nm)')
plt.show()