-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathfatigue.py
193 lines (151 loc) · 6.21 KB
/
fatigue.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
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
import numpy as np
import pandas as pd
from scipy.constants import convert_temperature
from . import temperature
def _avg_daily_temp_change(time_range, temp_cell):
"""
Helper function. Get the average of a year for the daily maximum temperature change.
For every 24hrs this function will find the delta between the maximum
temperature and minimun temperature. It will then take the deltas for
every day of the year and return the average delta.
Parameters
------------
time_range : timestamp series
Local time of specific site by the hour
year-month-day hr:min:sec . (Example) 2002-01-01 01:00:00
temp_cell : float series
Photovoltaic module cell temperature(Celsius) for every hour of a year
Returns
-------
avg_daily_temp_change : float
Average Daily Temerature Change for 1-year (Celsius)
avg_max_temp_cell : float
Average of Daily Maximum Temperature for 1-year (Celsius)
"""
if time_range.dtype == "object":
time_range = pd.to_datetime(time_range)
# Setup frame for vector processing
timeAndTemp_df = pd.DataFrame(columns=["Cell Temperature"])
timeAndTemp_df["Cell Temperature"] = temp_cell
timeAndTemp_df.index = time_range
timeAndTemp_df["month"] = timeAndTemp_df.index.month
timeAndTemp_df["day"] = timeAndTemp_df.index.day
# Group by month and day to determine the max and min cell Temperature [°C] for each day
dailyMaxCellTemp_series = timeAndTemp_df.groupby(["month", "day"])[
"Cell Temperature"
].max()
dailyMinCellTemp_series = timeAndTemp_df.groupby(["month", "day"])[
"Cell Temperature"
].min()
temp_cell_change = pd.DataFrame(
{"Max": dailyMaxCellTemp_series, "Min": dailyMinCellTemp_series}
)
temp_cell_change["TempChange"] = temp_cell_change["Max"] - temp_cell_change["Min"]
# Find the average temperature change for every day of one year [°C]
avg_daily_temp_change = temp_cell_change["TempChange"].mean()
# Find daily maximum cell temperature average
avg_max_temp_cell = dailyMaxCellTemp_series.mean()
return avg_daily_temp_change, avg_max_temp_cell
def _times_over_reversal_number(temp_cell, reversal_temp):
"""
Helper function. Get the number of times a temperature increases or decreases over a
specific temperature gradient.
Parameters
------------
temp_cell : float series
Photovoltaic module cell temperature [C]
reversal_temp : float
Temperature threshold to cross above and below [C]
Returns
--------
num_changes_temp_hist : int
Number of times the temperature threshold is crossed
"""
# Find the number of times the temperature crosses over 54.8(°C)
temp_df = pd.DataFrame()
temp_df["CellTemp"] = temp_cell
temp_df["COMPARE"] = temp_cell
temp_df["COMPARE"] = temp_df.COMPARE.shift(-1)
# reversal_temp = 54.8
temp_df["cross"] = (
((temp_df.CellTemp >= reversal_temp) & (temp_df.COMPARE < reversal_temp))
| ((temp_df.COMPARE > reversal_temp) & (temp_df.CellTemp <= reversal_temp))
| (temp_df.CellTemp == reversal_temp)
)
num_changes_temp_hist = temp_df.cross.sum()
return num_changes_temp_hist
def solder_fatigue(
weather_df,
meta,
time_range=None,
temp_cell=None,
reversal_temp=54.8,
n=1.9,
b=0.33,
C1=405.6,
Q=0.12,
):
"""
Get the Thermomechanical Fatigue of flat plate photovoltaic module solder joints.
Damage will be returned as the rate of solder fatigue for one year. Based on:
Bosco, N., Silverman, T. and Kurtz, S. (2020). Climate specific thermomechanical
fatigue of flat plate photovoltaic module solder joints. [online] Available
at: https://www.sciencedirect.com/science/article/pii/S0026271416300609
[Accessed 12 Feb. 2020].
This function uses the default values for 60-min input intervals from Table 4 of the above
paper. For other use cases, please refer to the paper for recommended values of C1 and
the reversal temperature.
Parameters
------------
weather_df : pd.dataframe
Must contain dni, dhi, ghi, temp_air, windspeed, and datetime index
meta : dict
site location meta-data
time_range : timestamp series, optional
Local time of specific site by the hour year-month-day hr:min:sec
(Example) 2002-01-01 01:00:00
If a time range is not give, function will use dt index from weather_df
temp_cell : float series, optional
Photovoltaic module cell temperature [C] for every hour of a year
reversal_temp : float, optional
Temperature threshold to cross above and below [C]
See the paper for other use cases
n : float
fit parameter for daily max temperature amplitude
b : float
fit parameter for reversal temperature
C1 : float
scaling constant, see the paper for details on appropriate values
Q : float
activation energy [eV]
Returns
--------
damage : float series
Solder fatigue damage for a time interval depending on time_range [kPa]
"""
# TODO this, and many other functions with temp_cell or temp_module would benefit from an
# optional parameter "conf = 'open_rack_glass_glass' or equivalent"
# TODO Make this function have more utility.
# People want to run all the scenarios from the bosco paper.
# Currently have everything hard coded for hourly calculation
# i.e. 405.6, 1.9, .33, .12
# Boltzmann Constant
k = 0.00008617333262145
# TODO detect sub-hourly time delta -> downsample to hourly
if time_range is None:
time_range = weather_df.index
if temp_cell is None:
temp_cell = temperature.cell(weather_df, meta)
temp_amplitude, temp_max_avg = _avg_daily_temp_change(time_range, temp_cell)
temp_max_avg = convert_temperature(temp_max_avg, "Celsius", "Kelvin")
num_changes_temp_hist = _times_over_reversal_number(temp_cell, reversal_temp)
damage = (
C1
* (temp_amplitude**n)
* (num_changes_temp_hist**b)
* np.exp(-(Q / (k * temp_max_avg)))
)
# Convert pascals to kilopascals
damage = damage / 1000
return damage
# TODO: add gaps functionality