forked from pvlib/pvlib-python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsnow.py
More file actions
187 lines (156 loc) · 6.95 KB
/
snow.py
File metadata and controls
187 lines (156 loc) · 6.95 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
"""
The ``snow`` module contains functions that model module snow cover and the
associated effects on PV module output
"""
import numpy as np
import pandas as pd
from pvlib.tools import sind
def _time_delta_in_hours(times):
delta = times.to_series().diff()
return delta.dt.total_seconds().div(3600)
def fully_covered_nrel(snowfall, threshold_snowfall=1.):
'''
Calculates the timesteps when the row's slant height is fully covered
by snow.
Parameters
----------
snowfall : Series
Accumulated snowfall in each time period [cm]
threshold_snowfall : float, default 1.0
Hourly snowfall above which snow coverage is set to the row's slant
height. [cm/hr]
Returns
----------
boolean: Series
True where the snowfall exceeds the defined threshold to fully cover
the panel.
Notes
-----
Implements the model described in [1]_ with minor improvements in [2]_.
References
----------
.. [1] Marion, B.; Schaefer, R.; Caine, H.; Sanchez, G. (2013).
"Measured and modeled photovoltaic system energy losses from snow for
Colorado and Wisconsin locations." Solar Energy 97; pp.112-121.
.. [2] Ryberg, D; Freeman, J. "Integration, Validation, and Application
of a PV Snow Coverage Model in SAM" (2017) NREL Technical Report
NREL/TP-6A20-68705
'''
timestep = _time_delta_in_hours(snowfall.index)
hourly_snow_rate = snowfall / timestep
# if we can infer a time frequency, use first snowfall value
# otherwise the first snowfall value is ignored
freq = pd.infer_freq(snowfall.index)
if freq is not None:
timedelta = pd.tseries.frequencies.to_offset(freq) / pd.Timedelta('1h')
hourly_snow_rate.iloc[0] = snowfall[0] / timedelta
else: # can't infer frequency from index
hourly_snow_rate[0] = 0 # replaces NaN
return hourly_snow_rate > threshold_snowfall
def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt,
initial_coverage=0, threshold_snowfall=1.,
can_slide_coefficient=-80., slide_amount_coefficient=0.197):
'''
Calculates the fraction of the slant height of a row of modules covered by
snow at every time step.
Implements the model described in [1]_ with minor improvements in [2]_,
with the change that the output is in fraction of the row's slant height
rather than in tenths of the row slant height. As described in [1]_, model
validation focused on fixed tilt systems.
Parameters
----------
snowfall : Series
Accumulated snowfall within each time period. [cm]
poa_irradiance : Series
Total in-plane irradiance [W/m^2]
temp_air : Series
Ambient air temperature [C]
surface_tilt : numeric
Tilt of module's from horizontal, e.g. surface facing up = 0,
surface facing horizon = 90. [degrees]
initial_coverage : float, default 0
Fraction of row's slant height that is covered with snow at the
beginning of the simulation. [unitless]
threshold_snowfall : float, default 1.0
Hourly snowfall above which snow coverage is set to the row's slant
height. [cm/hr]
can_slide_coefficient : float, default -80.
Coefficient to determine if snow can slide given irradiance and air
temperature. [W/(m^2 C)]
slide_amount_coefficient : float, default 0.197
Coefficient to determine fraction of snow that slides off in one hour.
[unitless]
Returns
-------
snow_coverage : Series
The fraction of the slant height of a row of modules that is covered
by snow at each time step.
Notes
-----
In [1]_, `can_slide_coefficient` is termed `m`, and the value of
`slide_amount_coefficient` is given in tenths of a module's slant height.
References
----------
.. [1] Marion, B.; Schaefer, R.; Caine, H.; Sanchez, G. (2013).
"Measured and modeled photovoltaic system energy losses from snow for
Colorado and Wisconsin locations." Solar Energy 97; pp.112-121.
.. [2] Ryberg, D; Freeman, J. (2017). "Integration, Validation, and
Application of a PV Snow Coverage Model in SAM" NREL Technical Report
NREL/TP-6A20-68705
'''
# find times with new snowfall
new_snowfall = fully_covered_nrel(snowfall, threshold_snowfall)
# set up output Series
snow_coverage = pd.Series(np.nan, index=poa_irradiance.index)
# determine amount that snow can slide in each timestep
can_slide = temp_air > poa_irradiance / can_slide_coefficient
slide_amt = slide_amount_coefficient * sind(surface_tilt) * \
_time_delta_in_hours(poa_irradiance.index)
slide_amt[~can_slide] = 0.
# don't slide during snow events
slide_amt[new_snowfall] = 0.
# don't slide in the interval preceding the snowfall data
slide_amt.iloc[0] = 0
# build time series of cumulative slide amounts
sliding_period_ID = new_snowfall.cumsum()
cumulative_sliding = slide_amt.groupby(sliding_period_ID).cumsum()
# set up time series of snow coverage without any sliding applied
snow_coverage[new_snowfall] = 1.0
if np.isnan(snow_coverage.iloc[0]):
snow_coverage.iloc[0] = initial_coverage
snow_coverage.ffill(inplace=True)
snow_coverage -= cumulative_sliding
# clean up periods where row is completely uncovered
return snow_coverage.clip(lower=0)
def dc_loss_nrel(snow_coverage, num_strings):
'''
Calculates the fraction of DC capacity lost due to snow coverage.
DC capacity loss assumes that if a string is partially covered by snow,
the string's capacity is lost; see [1]_, Eq. 11.8.
Module orientation is accounted for by specifying the number of cell
strings in parallel along the slant height.
For example, a typical 60-cell module has 3 parallel strings, each
comprising 20 cells in series, with the cells arranged in 6 columns of 10
cells each. For a row consisting of single modules, if the module is
mounted in portrait orientation, i.e., the row slant height is along a
column of 10 cells, there is 1 string in parallel along the row slant
height, so `num_strings=1`. In contrast, if the module is mounted in
landscape orientation with the row slant height comprising 6 cells, there
are 3 parallel strings along the row slant height, so `num_strings=3`.
Parameters
----------
snow_coverage : numeric
The fraction of row slant height covered by snow at each time step.
num_strings: int
The number of parallel-connected strings along a row slant height.
Returns
-------
loss : numeric
fraction of DC capacity loss due to snow coverage at each time step.
References
----------
.. [1] Gilman, P. et al., (2018). "SAM Photovoltaic Model Technical
Reference Update", NREL Technical Report NREL/TP-6A20-67399.
Available at https://www.nrel.gov/docs/fy18osti/67399.pdf
'''
return np.ceil(snow_coverage * num_strings) / num_strings