-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdemo.py
More file actions
279 lines (238 loc) · 10 KB
/
demo.py
File metadata and controls
279 lines (238 loc) · 10 KB
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
"""
Partial flex, partial explanantion of how it works.
"""
import numpy as np
from PV import System, calculate_system_performance
from shading import calculate_crop_shading, calculate_self_shading
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from __utils__ import (
Cell,
Opaque,
Array,
plot_curves,
_Bypass_Diode,
Bifacial,
generate_times,
)
header_string = """
\033[34m
________________________________________________________________________
| _____| | | | ___ | ___ \| | \ | | ___ || |
| | | | | | | | | | | | | |\ \ | | | |__|| |
| |_____| |___| | |___| | | | | | | \ \ | | | ___ | |
|_____ | ___ | ___ | | | | | | \ \| | | |_ ||__|
_____| | | | | | | | |___| | | | \ | |___| | __
|________|__| |__|__| |__|________/|__|__| \___|_________||__|
____________ ____________
/ / / /
/ / / /
/ / / /
/ / / /
/ / / /
/___________/ /___________/
| |
| .............. | ..............
| - - - - - - \..............\ | - - - - - - \..............\
\033[34m
"""
print(header_string)
input("Press Enter to continue")
# Part 1: Inputing/generating information
# Generates hourly times in desired structure for sun angles and thus every shading function
print(
"\nThe hourly times must be input very specifically into the shading functions. \nThe generate_times function does that work\n "
)
Time = generate_times(2019, 1, 1, 1, 2019, 1, 2, 0)
# Necessary location data. Only longitude, latitude and solar irradiance per hour
latitude = 5.5515
longitude = 7.4175
print(
f"\nModelling at a latitude of {latitude} and longitude of {longitude}. \nThis is the location of a village called Ekenobizi in south east Nigeria\n"
)
irradiance = np.loadtxt("solar_irradiance.csv", dtype=str, delimiter=",")
irradiance = irradiance[:, 1].astype(float)
irradiance = irradiance[: np.shape(Time[0])[0]]
print("Solar irradiance data is take from Renewables Ninja \n")
# Part 2: Inputing cell, bypass diode, module, array and system data
print(
"Cells, bypass diodes, modules, arrays and systems must all have their information input.\n \n"
)
cell_1 = Cell(
xlength=152,
ylength=152,
max_current=10,
bd_volt=-15,
bd_factor=2e-3,
bd_exp=3,
alpha_sc=0.0042,
I_L_ref=8.24,
I_o_ref=2.36e-9,
R_sh_ref=1000,
R_s=0.00181,
)
cell_2 = Cell(
xlength=152,
ylength=152,
max_current=1,
bd_volt=-3,
bd_factor=2e-3,
bd_exp=3,
alpha_sc=0.0042,
I_L_ref=8.24,
I_o_ref=2.36e-9,
R_sh_ref=1000,
R_s=0.00181,
)
bypass_1 = _Bypass_Diode(16, connected_horizontal=False)
bypass_2 = _Bypass_Diode(16, connected_horizontal=True)
module_1 = Opaque(8, 8, 0.5, 45, cell=cell_1, bypass_diode=bypass_2)
module_2 = Bifacial(8, 8, 0.5, 90, cell_1, back_cell=cell_2, bypass_diode=bypass_1)
PV_1 = Array(
latitude, longitude, 7, 10, 0.15, 1, 90, module_1, "Opaque Array", timeZone=1
)
PV_2 = Array(
latitude, longitude, 5, 8, 0.1, 1, 0, module_2, "Bifacial Array", timeZone=1
)
array_sys = System([PV_1, PV_2])
input("Press Enter to continue to Self Shading!")
# Part 3: Self Shading
print(
"\nAfter inputing data you can use the self shading function goes to find the shade and irradiance of the each cell of each module in the array \n"
)
PV_1_dicts = calculate_self_shading(PV_1, irradiance, Time, plot_graphs=False)
print(
f"For the {PV_1.Module.type} array, the self shading function returns a dictionary with these keys: {PV_1_dicts.keys()} \n"
)
PV_2_dicts = calculate_self_shading(PV_2, irradiance, Time, plot_graphs=False)
print(
f"For the {PV_2.Module.type} array, the self shading function returns a dictionary with these keys: {PV_2_dicts.keys()} \n"
)
print(
f"Each of these keys map to another dictionary that maps the module to arrays that have the illumination for each cell for each hour.\nTherefore the dict illumination has the keys {PV_1_dicts['illumination'].keys()} for each module in a row of the array"
)
print(
"Except for the first row all rows are identical so only the shading of the first two rows need to be considered. \nIf bifacial however the back of the back row should also be considered as it won't be shaded either."
)
input("Press Enter to continue to Power Generation!")
# Part 4: Power generation (Cell, Module and Array)
print("The cell, module and array all have inbuilt methods to calculate their power.\n")
labels = np.unique(np.round(PV_1_dicts["illumination"]["Module 2"][10], 2)).astype(
"str"
)
labels = np.char.add(labels, " W/m^2").tolist()
cell_IVs = cell_1.find_IVs(
np.unique(PV_1_dicts["illumination"]["Module 2"][10]), 22, return_as_dfs=True
)
plot_curves(
cell_IVs, labels, f"Cell IVs at irradiances of Module 2 of {PV_1.name} at 10am"
)
plt.show()
print(
"From the cell IVs we can find the output power of any module using the module's class method module_performance."
)
print(
f" Module 2 in the {PV_1.name} produces {PV_1.Module.module_performance(PV_1_dicts["illumination"]["Module 2"][10], 25)} W at 10am"
)
print(
f" Module 2 in the {PV_2.name} produces {PV_2.Module.module_performance(PV_2_dicts["illumination"]["Module 2"][10], 25, back_irradiance=PV_2_dicts["back illumination"]["Module 2"][10])} W at 10am"
)
print(
"For Bifacial Modules the module performance method takes an extra argument - back_irradiance"
)
print(
"To find the array power, module performance summed across modules for each point in time and output in the array.\n"
)
print(
f" At 10 am the {PV_1.name} produces {PV_1.calculate_output_power(PV_1_dicts['illumination'], PV_1_dicts['front row illumination'], 25)[10]} W\n"
)
print(
f" At 10 am the {PV_2.name} produces {PV_2.calculate_output_power(PV_2_dicts['illumination'], PV_2_dicts['front row illumination'], 25, PV_2_dicts['back illumination'], PV_2_dicts['back row illumination'])[10]} W"
)
print(
"For the bifacial arrays the output power should also take in a back illumination and back row illumination\n"
)
input("Press Enter to continue to Crop Shading!")
# Part 5: Crop Shading
# ground_coordinates give the area around the pv array to calculate crop shading
print(
"\nWe can calculate the on the ground shading using the other function in shading.py: calculate_crop_shading\n"
)
ground_coordinates = np.array([[-5, -5], [-5, 18], [18, 18], [18, -5]])
print(
f"For the {PV_1.name} I set the ground coordinates to {ground_coordinates}. This is an optional feature of the function. \nGround coordinates are measured from the bottom left corner of the bottom left module and input anticlockwise."
)
crop_shading_1, crop_illum_1 = calculate_crop_shading(
PV_1, irradiance, Time, resolution=0.1, ground_coordinates=ground_coordinates
)
crop_shading_2, crop_illum_2 = calculate_crop_shading(
PV_1, irradiance, Time, resolution=0.1
)
print(
f"\nIf ground coordinates are not specificed they measure a little bit more than the area of the array. \nCompare the area considered between {PV_1.name} and {PV_2.name}"
)
# Plotting average crop illumination
plt.imshow(np.transpose(np.average(crop_illum_1, 0)), cmap="inferno", origin="lower")
plt.colorbar()
plt.title(
f"Average solar irradiance of {PV_1.name}"
)
plt.show()
plt.imshow(np.transpose(np.average(crop_illum_2, 0)), cmap="inferno", origin="lower")
plt.colorbar()
plt.title(
f"Average solar irradiance of {PV_2.name}"
)
plt.show()
# Animation of hourly shadows
fig, ax = plt.subplots()
heatmap = ax.imshow(
np.transpose(crop_illum_1[0]),
cmap="inferno",
origin="lower",
vmin=0,
vmax=np.max(crop_illum_1),
)
plt.colorbar(heatmap, ax=ax)
plt.title(f"Animation of solar irradiance of {PV_1.name}")
def nextGraph(i):
heatmap.set_data(np.transpose(crop_illum_1[i]))
return [heatmap]
animate = animation.FuncAnimation(
fig, nextGraph, frames=crop_illum_1.shape[0], interval=200, blit=True
)
plt.show()
fig, ax = plt.subplots()
heatmap = ax.imshow(
np.transpose(crop_illum_2[0]),
cmap="inferno",
origin="lower",
vmin=0,
vmax=np.max(crop_illum_2),
)
plt.colorbar(heatmap, ax=ax)
plt.title(f"Animation of ground solar irradiance of {PV_2.name} ")
def nextGraph(i):
heatmap.set_data(np.transpose(crop_illum_2[i]))
return [heatmap]
animate = animation.FuncAnimation(
fig, nextGraph, frames=crop_illum_2.shape[0], interval=200, blit=True
)
plt.show()
input("\nPress Enter to continue to System Modelling!\n")
# Part 6: System Modelling
print(
"If you create a system of arrays you can use the calculate system performance method to manage which of the above functions you wan to use.\n"
)
sys_power, array_pow_dict, crop_illum_dict = calculate_system_performance(
array_sys,
irradiance,
Time,
25,
resolution=0.1,
ground_coordinates=ground_coordinates,
)
print(
f"If we make a system out of the {PV_1.name} and {PV_2.name} we have a power of {sys_power[10]} at 10am which you will see is just the sum of the array powers.\nAdditionally the method outputs a dictionary for both the array power that maps each array to the power it produces at each time and a dictionary for the crop shading if the user specifies to use it."
)
print("Demo complete! I hope you have a better understanding of this module :)")