Skip to content

Commit 3661d86

Browse files
committed
rename functions, review responses, add subhourly test
1 parent 8343212 commit 3661d86

File tree

2 files changed

+52
-41
lines changed

2 files changed

+52
-41
lines changed

pvlib/snowcoverage.py

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010

1111
def _time_delta_in_hours(times):
1212
delta = times.to_series().diff()
13-
return delta.dt.seconds.div(3600)
13+
return delta.dt.total_seconds.div(3600)
1414

1515

16-
def fully_covered(snowfall, threshold=1.):
16+
def snow_nrel_fully_covered(snowfall, threshold=1.):
1717
'''
1818
Calculates the timesteps when the row's slant height is fully covered
1919
by snow.
@@ -39,29 +39,32 @@ def fully_covered(snowfall, threshold=1.):
3939
References
4040
----------
4141
.. [1] Marion, B.; Schaefer, R.; Caine, H.; Sanchez, G. (2013).
42-
Measured and modeled photovoltaic system energy losses from snow for
43-
Colorado and Wisconsin locations. Solar Energy 97; pp.112-121.
42+
"Measured and modeled photovoltaic system energy losses from snow for
43+
Colorado and Wisconsin locations." Solar Energy 97; pp.112-121.
4444
.. [2] Ryberg, D; Freeman, J. "Integration, Validation, and Application
4545
of a PV Snow Coverage Model in SAM" (2017) NREL Technical Report
4646
'''
47-
delta = snowfall.index.to_series().diff() # [0] will be NaT
48-
timestep = delta.dt.seconds.div(3600) # convert to hours
47+
timestep = _time_delta_in_hours(snowfall.index)
4948
time_adjusted = snowfall / timestep
5049
time_adjusted.iloc[0] = 0 # replace NaN from NaT / timestep
5150
return time_adjusted >= threshold
5251

5352

54-
def snow_coverage_nrel(snowfall, poa_irradiance, temperature, surface_tilt,
55-
threshold_snowfall=1., m=-80,
56-
sliding_coefficient=0.197):
53+
def snow_nrel(snowfall, poa_irradiance, temperature, surface_tilt,
54+
threshold_snowfall=1., m=-80, sliding_coefficient=0.197):
5755
'''
5856
Calculates the fraction of the slant height of a row of modules covered by
5957
snow at every time step.
6058
59+
Initial snow coverage is assumed to be zero. Implements the model described
60+
in [1]_ with minor improvements in [2]_, with the change that the output
61+
is in fraction of the row's slant height rather than in tenths of the row
62+
slant height. Validated for fixed tilt systems.
63+
6164
Parameters
6265
----------
6366
snowfall : Series
64-
Accumulated snowfall at the end of each time period. [cm]
67+
Accumulated snowfall within each time period. [cm]
6568
poa_irradiance : Series
6669
Total in-plane irradiance [W/m^2]
6770
temperature : Series
@@ -71,36 +74,33 @@ def snow_coverage_nrel(snowfall, poa_irradiance, temperature, surface_tilt,
7174
surface facing horizon = 90. Must be between 0 and 180. [degrees]
7275
threshold_snowfall : float, default 1.0
7376
Minimum hourly snowfall to cover a row's slant height. [cm/hr]
74-
m : numeric
75-
A coefficient used in the model described in [1]_. [W/(m^2 C)]
76-
sliding coefficient : numeric
77-
Empirically determined coefficient used in [1]_ to determine how much
77+
m : float, default -80.
78+
Coefficient used in [1]_ to determine if snow can slide given
79+
irradiance and air temperature. [W/(m^2 C)]
80+
sliding coefficient : float, default 0.197
81+
Empirical coefficient used in [1]_ to determine how much
7882
snow slides off in each time period. [unitless]
7983
8084
Returns
8185
-------
82-
snow_coverage : numeric
83-
The fraction of a the slant height of a row of modules that is covered
86+
snow_coverage : Series
87+
The fraction of the slant height of a row of modules that is covered
8488
by snow at each time step.
8589
86-
Notes
87-
-----
88-
Initial snow coverage is assumed to be zero. Implements the model described
89-
in [1]_ with minor improvements in [2]_. Validated for fixed tilt systems.
90-
9190
References
9291
----------
9392
.. [1] Marion, B.; Schaefer, R.; Caine, H.; Sanchez, G. (2013).
94-
Measured and modeled photovoltaic system energy losses from snow for
95-
Colorado and Wisconsin locations. Solar Energy 97; pp.112-121.
93+
"Measured and modeled photovoltaic system energy losses from snow for
94+
Colorado and Wisconsin locations." Solar Energy 97; pp.112-121.
9695
.. [2] Ryberg, D; Freeman, J. (2017). "Integration, Validation, and
9796
Application of a PV Snow Coverage Model in SAM" NREL Technical Report
98-
NREL/TP-6A20-68705
97+
NREL/TP-6A20-68705
9998
'''
10099

101100
# set up output Series
102101
snow_coverage = pd.Series(index=poa_irradiance.index, data=np.nan)
103-
snow_events = snowfall[fully_covered(snowfall, threshold_snowfall)]
102+
snow_events = snowfall[snow_nrel_fully_covered(snowfall,
103+
threshold_snowfall)]
104104

105105
can_slide = temperature > poa_irradiance / m
106106
slide_amt = sliding_coefficient * sind(surface_tilt) * \
@@ -109,8 +109,7 @@ def snow_coverage_nrel(snowfall, poa_irradiance, temperature, surface_tilt,
109109
uncovered = pd.Series(0.0, index=poa_irradiance.index)
110110
uncovered[can_slide] = slide_amt[can_slide]
111111

112-
windows = [(ev, ne) for (ev, ne) in
113-
zip(snow_events.index[:-1], snow_events.index[1:])]
112+
windows = list(zip(snow_events.index[:-1], snow_events.index[1:]))
114113
# add last time window
115114
windows.append((snow_events.index[-1], snowfall.index[-1]))
116115

@@ -125,7 +124,7 @@ def snow_coverage_nrel(snowfall, poa_irradiance, temperature, surface_tilt,
125124
return snow_coverage
126125

127126

128-
def snow_loss_factor(snow_coverage, num_strings):
127+
def snow_nrel_dc_loss(snow_coverage, num_strings):
129128
'''
130129
Calculates the DC loss due to snow coverage. Assumes that if a string is
131130
partially covered by snow, it produces 0W.

pvlib/tests/test_snowcoverage.py

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,45 +7,57 @@
77
from pvlib.tools import sind
88

99

10-
def test_fully_covered():
10+
def test_snow_nrel_fully_covered():
1111
dt = pd.date_range(start="2019-1-1 12:00:00", end="2019-1-1 18:00:00",
1212
freq='1h')
1313
snowfall_data = pd.Series([1, 5, .6, 4, .23, -5, 19], index=dt)
1414
expected = pd.Series([False, True, False, True, False, False, True],
1515
index=dt)
16-
actual_snowfall = snowcoverage.fully_covered(snowfall_data)
16+
actual_snowfall = snowcoverage.snow_nrel_fully_covered(snowfall_data)
1717
assert_series_equal(actual_snowfall, expected)
1818

1919

20-
def test_snow_coverage_nrel():
20+
def test_snow_nrel_hourly():
2121
surface_tilt = 45
2222
sliding_coefficient = 0.197
2323
dt = pd.date_range(start="2019-1-1 10:00:00", end="2019-1-1 17:00:00",
2424
freq='1h')
25-
poa_irradiance = pd.Series([400, 200, 100, 1234, 134, 982, 100, 100], index=dt)
25+
poa_irradiance = pd.Series([400, 200, 100, 1234, 134, 982, 100, 100],
26+
index=dt)
2627
temperature = pd.Series([10, 2, 10, 1234, 34, 982, 10, 10], index=dt)
2728
slide_amt = sliding_coefficient * sind(surface_tilt)
2829
snowfall_data = pd.Series([1, .5, .6, .4, .23, -5, .1, .1], index=dt)
29-
snow_coverage = snowcoverage.snow_coverage_nrel(
30+
snow_coverage = snowcoverage.snow_nrel(
3031
snowfall_data, poa_irradiance, temperature, surface_tilt,
31-
threshold_snowfall=0.0)
32-
# covered every hour except when snowfall = -5
33-
expected = pd.Series([1., 1., 1., 1., 1., 1. - slide_amt, 1., 1.], index=dt)
32+
threshold_snowfall=0.6)
33+
covered = np.append(np.array([0., 0.]),
34+
1.0 - slide_amt * np.array([0, 1, 2, 3, 4, 5]))
35+
expected = pd.Series(covered, index=dt)
3436
assert_series_equal(snow_coverage, expected)
35-
# snowfall_threshold = 0.6
36-
snow_coverage = snowcoverage.snow_coverage_nrel(
37+
38+
39+
def test_snow_nrel_subhourly():
40+
surface_tilt = 45
41+
sliding_coefficient = 0.197
42+
dt = pd.date_range(start="2019-1-1 11:00:00", end="2019-1-1 14:00:00",
43+
freq='15T')
44+
poa_irradiance = pd.Series([400, 200, 100, 1234, 134, 982, 100, 100],
45+
index=dt)
46+
temperature = pd.Series([10, 2, 10, 1234, 34, 982, 10, 10], index=dt)
47+
slide_amt = sliding_coefficient * sind(surface_tilt)
48+
snowfall_data = pd.Series([1, .5, .6, .4, .23, -5, .1, .1], index=dt)
49+
snow_coverage = snowcoverage.snow_nrel(
3750
snowfall_data, poa_irradiance, temperature, surface_tilt,
3851
threshold_snowfall=0.6)
39-
# covered every hour except when snowfall = -5
4052
covered = np.append(np.array([0., 0.]),
4153
1.0 - slide_amt * np.array([0, 1, 2, 3, 4, 5]))
4254
expected = pd.Series(covered, index=dt)
4355
assert_series_equal(snow_coverage, expected)
4456

4557

46-
def test_snow_loss_factor():
58+
def test_snow_nrel_dc_loss():
4759
num_strings = 8
4860
snow_coverage = pd.Series([1, 1, .5, .6, .2, .4, 0])
4961
expected = pd.Series([1, 1, .5, .625, .25, .5, 0])
50-
actual = snowcoverage.snow_loss_factor(snow_coverage, num_strings)
62+
actual = snowcoverage.snow_nrel_dc_loss(snow_coverage, num_strings)
5163
assert_series_equal(expected, actual)

0 commit comments

Comments
 (0)