Skip to content

Commit 3594450

Browse files
committed
add snow_depth inputs
1 parent afdcdd4 commit 3594450

File tree

2 files changed

+63
-9
lines changed

2 files changed

+63
-9
lines changed

pvlib/snow.py

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,30 +13,36 @@ def _time_delta_in_hours(times):
1313
return delta.dt.total_seconds().div(3600)
1414

1515

16-
def fully_covered_nrel(snowfall, threshold_snowfall=1.):
16+
def fully_covered_nrel(snowfall, snow_depth=None, threshold_snowfall=1.):
1717
'''
1818
Calculates the timesteps when the row's slant height is fully covered
1919
by snow.
2020
2121
Parameters
2222
----------
23-
snowfall : Series
24-
Accumulated snowfall in each time period [cm]
25-
26-
threshold_snowfall : float, default 1.0
23+
snowfall: Series
24+
Accumulated snowfall in each time period. [cm]
25+
snow_depth: Series, optional
26+
Snow depth on the ground at the beginning of each time period.
27+
Must have the same index as `snowfall`. [cm]
28+
threshold_snowfall: float, default 1.0
2729
Hourly snowfall above which snow coverage is set to the row's slant
2830
height. [cm/hr]
2931
3032
Returns
3133
----------
32-
boolean: Series
34+
Series
3335
True where the snowfall exceeds the defined threshold to fully cover
3436
the panel.
3537
3638
Notes
3739
-----
3840
Implements the model described in [1]_ with minor improvements in [2]_.
3941
42+
`snow_depth` is used to set coverage=0 when no snow is present on the
43+
ground. This check is described in [2]_ as needed for systems with
44+
low tilt angle.
45+
4046
References
4147
----------
4248
.. [1] Marion, B.; Schaefer, R.; Caine, H.; Sanchez, G. (2013).
@@ -56,11 +62,15 @@ def fully_covered_nrel(snowfall, threshold_snowfall=1.):
5662
hourly_snow_rate.iloc[0] = snowfall.iloc[0] / timedelta
5763
else: # can't infer frequency from index
5864
hourly_snow_rate.iloc[0] = 0 # replaces NaN
59-
return hourly_snow_rate > threshold_snowfall
65+
covered = (hourly_snow_rate > threshold_snowfall)
66+
# no coverage when no snow on the ground
67+
if snow_depth is not None:
68+
covered = covered & (snow_depth > 0.)
69+
return covered
6070

6171

6272
def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt,
63-
initial_coverage=0, threshold_snowfall=1.,
73+
snow_depth=None, initial_coverage=0, threshold_snowfall=1.,
6474
can_slide_coefficient=-80., slide_amount_coefficient=0.197):
6575
'''
6676
Calculates the fraction of the slant height of a row of modules covered by
@@ -82,6 +92,9 @@ def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt,
8292
surface_tilt : numeric
8393
Tilt of module's from horizontal, e.g. surface facing up = 0,
8494
surface facing horizon = 90. [degrees]
95+
snow_depth : Series, optional
96+
Snow depth on the ground at the beginning of each time period.
97+
Must have the same index as `snowfall`. [cm]
8598
initial_coverage : float, default 0
8699
Fraction of row's slant height that is covered with snow at the
87100
beginning of the simulation. [unitless]
@@ -106,6 +119,10 @@ def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt,
106119
In [1]_, `can_slide_coefficient` is termed `m`, and the value of
107120
`slide_amount_coefficient` is given in tenths of a module's slant height.
108121
122+
`snow_depth` is used to set coverage=0 when no snow is present on the
123+
ground. This check is described in [2]_ as needed for systems with
124+
low tilt angle.
125+
109126
References
110127
----------
111128
.. [1] Marion, B.; Schaefer, R.; Caine, H.; Sanchez, G. (2013).
@@ -117,7 +134,7 @@ def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt,
117134
'''
118135

119136
# find times with new snowfall
120-
new_snowfall = fully_covered_nrel(snowfall, threshold_snowfall)
137+
new_snowfall = fully_covered_nrel(snowfall, snow_depth, threshold_snowfall)
121138

122139
# set up output Series
123140
snow_coverage = pd.Series(np.nan, index=poa_irradiance.index)
@@ -143,6 +160,10 @@ def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt,
143160
snow_coverage.ffill(inplace=True)
144161
snow_coverage -= cumulative_sliding
145162

163+
if snow_depth is not None:
164+
# no coverage when there's no snow on the ground
165+
# described in [2] to avoid non-sliding snow for low-tilt systems.
166+
snow_coverage[snow_depth<=0] = 0.
146167
# clean up periods where row is completely uncovered
147168
return snow_coverage.clip(lower=0)
148169

pvlib/tests/test_snow.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,18 @@ def test_fully_covered_nrel():
1919
assert_series_equal(expected, fully_covered)
2020

2121

22+
def test_fully_covered_nrel_with_snow_depth():
23+
dt = pd.date_range(start="2019-1-1 12:00:00", end="2019-1-1 18:00:00",
24+
freq='1h')
25+
snowfall_data = pd.Series([1, 5, .6, 4, .23, -5, 19], index=dt)
26+
snow_depth = pd.Series([0., 1, 6, 6.6, 10.6, 10., -2], index=dt)
27+
expected = pd.Series([False, True, False, True, False, False, False],
28+
index=dt)
29+
fully_covered = snow.fully_covered_nrel(snowfall_data,
30+
snow_depth=snow_depth)
31+
assert_series_equal(expected, fully_covered)
32+
33+
2234
def test_coverage_nrel_hourly():
2335
surface_tilt = 45
2436
slide_amount_coefficient = 0.197
@@ -38,6 +50,27 @@ def test_coverage_nrel_hourly():
3850
assert_series_equal(expected, snow_coverage)
3951

4052

53+
def test_coverage_nrel_hourly_with_snow_depth():
54+
surface_tilt = 45
55+
slide_amount_coefficient = 0.197
56+
dt = pd.date_range(start="2019-1-1 10:00:00", end="2019-1-1 17:00:00",
57+
freq='1h')
58+
poa_irradiance = pd.Series([400, 200, 100, 1234, 134, 982, 100, 100],
59+
index=dt)
60+
temp_air = pd.Series([10, 2, 10, 1234, 34, 982, 10, 10], index=dt)
61+
snowfall_data = pd.Series([1, .5, .6, .4, .23, -5, .1, .1], index=dt)
62+
snow_depth = pd.Series([1, 1, 1, 1, 0, 1, 0, .1], index=dt)
63+
snow_coverage = snow.coverage_nrel(
64+
snowfall_data, poa_irradiance, temp_air, surface_tilt,
65+
snow_depth=snow_depth, threshold_snowfall=0.6)
66+
67+
slide_amt = slide_amount_coefficient * sind(surface_tilt)
68+
covered = 1.0 - slide_amt * np.array([0, 1, 2, 3, 4, 5, 6, 7])
69+
expected = pd.Series(covered, index=dt)
70+
expected[snow_depth<=0] = 0
71+
assert_series_equal(expected, snow_coverage)
72+
73+
4174
def test_coverage_nrel_subhourly():
4275
surface_tilt = 45
4376
slide_amount_coefficient = 0.197

0 commit comments

Comments
 (0)