@@ -187,24 +187,23 @@ def dc_loss_nrel(snow_coverage, num_strings):
187187 return np .ceil (snow_coverage * num_strings ) / num_strings
188188
189189
190- def _townsend_Se ( S , N ):
190+ def _townsend_effective_snow ( snow_load , snow_events ):
191191 '''
192- Calculates effective snow for a given month based upon the total snowfall
193- received in a month in inches and the number of events where snowfall is
194- greater than 1 inch
192+ Calculates effective snow using the total snowfall in inches received
193+ each month and the number of snowfall events each month.
195194
196195 Parameters
197196 ----------
198- S : numeric
199- Snowfall in inches received in a month [in]
197+ snow_load : array-like
198+ Inches of snow received each month. Referred to as S in the paper [in]
200199
201- N: numeric
202- Number of snowfall events with snowfall > 1" [-]
200+ snow_events : array-like
201+ Number of snowfall events each month. Referred to as N in the paper [-]
203202
204203 Returns
205204 -------
206- effective_snowfall : numeric
207- Effective snowfall as defined in the townsend model
205+ effective_snowfall : array-like
206+ Effective snowfall as defined in the Townsend model
208207
209208 References
210209 ----------
@@ -215,52 +214,56 @@ def _townsend_Se(S, N):
215214 Available at https://www.researchgate.net/publication/261042016_Photovoltaics_and_snow_An_update_from_two_winters_of_measurements_in_the_SIERRA
216215
217216 ''' # noqa: E501
218- return (np .where (N > 0 , 0.5 * S * (1 + 1 / N ), 0 ))
217+ snow_events_no_zeros = np .maximum (snow_events , 1 )
218+ effective_snow = 0.5 * snow_load * (1 + 1 / snow_events_no_zeros )
219+ return np .where (snow_events > 0 , effective_snow , 0 )
219220
220221
221222def loss_townsend (snow_total , snow_events , surface_tilt , relative_humidity ,
222223 temp_air , poa_global , slant_height , lower_edge_drop_height ,
223224 angle_of_repose = 40 ):
224225 '''
225- Calculates monthly snow loss based on a generalized monthly snow loss model
226- discussed in [1]_.
226+ Calculates monthly snow loss based on the Townsend monthly snow loss model [1]_.
227227
228228 Parameters
229229 ----------
230- snow_total : numeric
231- Inches of snow received in the current month. Referred as S in the
232- paper [in]
230+ snow_total : array-like
231+ Inches of snow received each month. Referred to as S in the paper [in]
233232
234- snow_events : numeric
235- Number of snowfall events with snowfall > 1". Referred as N in the
236- paper [-]
233+ snow_events : array-like
234+ Number of snowfall events each month. Referred to as N in the paper [-]
237235
238- surface_tilt : numeric
239- Array surface_tilt [deg]
236+ surface_tilt : float
237+ Tilt angle of the array [deg]
240238
241- relative_humidity : numeric
242- Relative humidity [%]
239+ relative_humidity : array-like
240+ Monthly average relative humidity [%]
243241
244- temp_air : numeric
245- Ambient temperature [° C]
242+ temp_air : array-like
243+ Monthly average ambient temperature [C]
246244
247- poa_global : numeric
248- Plane of array insolation [kWh/m2/month]
245+ poa_global : array-like
246+ Monthly plane of array insolation [kWh/m2/month]
249247
250248 slant_height : float
251249 Row length in the slanted plane of array dimension [in]
252250
253251 lower_edge_drop_height : float
254252 Drop height from array edge to ground [in]
255253
256- P : float
257- piled snow angle, assumed to stabilize at 40° , the midpoint of
254+ angle_of_repose : float, default 40
255+ piled snow angle, assumed to stabilize at 40°, the midpoint of
258256 25°-55° avalanching slope angles [deg]
259257
260258 Returns
261259 -------
262- loss : numeric
263- Average monthly DC capacity loss due to snow coverage [%]
260+ loss : array-like
261+ Monthly average DC capacity loss fraction due to snow coverage
262+
263+ Notes
264+ -----
265+ This model has not been validated for tracking arrays; however, for tracking
266+ arrays [1]_ suggests using the maximum rotation angle in place of surface_tilt.
264267
265268 References
266269 ----------
@@ -277,16 +280,30 @@ def loss_townsend(snow_total, snow_events, surface_tilt, relative_humidity,
277280 snow_total_prev = np .roll (snow_total , 1 )
278281 snow_events_prev = np .roll (snow_events , 1 )
279282
280- Se = _townsend_Se (snow_total , snow_events )
281- Se_prev = _townsend_Se (snow_total_prev , snow_events_prev )
282-
283- Se_weighted = 1 / 3 * Se_prev + 2 / 3 * Se
284- gamma = (slant_height * Se_weighted * cosd (surface_tilt )) / \
285- (np .clip ((lower_edge_drop_height ** 2 - Se_weighted ** 2 ), a_min = 0.01 ,
286- a_max = None ) / 2 / tand (angle_of_repose ))
287-
288- GIT = 1 - C2 * np .exp (- gamma )
289- loss = (C1 * Se_weighted * (cosd (surface_tilt ))** 2 * GIT *
290- relative_humidity / (temp_air + 273.15 )** 2 / poa_global ** 0.67 ) / 100
291-
292- return loss
283+ effective_snow = _townsend_effective_snow (snow_total , snow_events )
284+ effective_snow_prev = _townsend_effective_snow (snow_total_prev , snow_events_prev )
285+ effective_snow_weighted = 1 / 3 * effective_snow_prev + 2 / 3 * effective_snow
286+
287+ drop_height_clipped = np .maximum (lower_edge_drop_height , 0.01 )
288+ gamma = (
289+ slant_height
290+ * effective_snow_weighted
291+ * cosd (surface_tilt )
292+ / (drop_height_clipped ** 2 - effective_snow_weighted ** 2 )
293+ * 2
294+ * tand (angle_of_repose )
295+ )
296+
297+ ground_interference_term = 1 - C2 * np .exp (- gamma )
298+ temp_air_kelvin = temp_air + 273.15
299+ loss_percentage = (
300+ C1
301+ * effective_snow_weighted
302+ * cosd (surface_tilt )** 2
303+ * ground_interference_term
304+ * relative_humidity
305+ / temp_air_kelvin ** 2
306+ / poa_global ** 0.67
307+ )
308+
309+ return loss_percentage / 100
0 commit comments