@@ -13,30 +13,38 @@ 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. ,
17+ threshold_depth = 1. ):
1718 '''
18- Calculates the timesteps when the row's slant height is fully covered
19- by snow.
19+ Calculates the timesteps when modules are fully covered 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
27- Hourly snowfall above which snow coverage is set to the row's slant
28- height. [cm/hr]
23+ snowfall: Series
24+ 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
29+ Hourly snowfall above which the row is fully covered for that hour.
30+ [cm/hr]
31+ threshold_depth: float, default 1.0
32+ Snow depth on the ground, above which snow can affect the modules. [cm]
2933
3034 Returns
3135 ----------
32- boolean : Series
33- True where the snowfall exceeds the defined threshold to fully cover
34- the panel.
36+ covered : Series
37+ A Series of boolean, True where the snowfall exceeds the defined
38+ threshold to fully cover the panel.
3539
3640 Notes
3741 -----
3842 Implements the model described in [1]_ with minor improvements in [2]_.
3943
44+ ``snow_depth`` is used to return `False` (not fully covered) when snow
45+ is less than ``threshold_depth``. This check is described in [2]_ as needed
46+ for systems with low tilt angle.
47+
4048 References
4149 ----------
4250 .. [1] Marion, B.; Schaefer, R.; Caine, H.; Sanchez, G. (2013).
@@ -56,15 +64,20 @@ def fully_covered_nrel(snowfall, threshold_snowfall=1.):
5664 hourly_snow_rate .iloc [0 ] = snowfall .iloc [0 ] / timedelta
5765 else : # can't infer frequency from index
5866 hourly_snow_rate .iloc [0 ] = 0 # replaces NaN
59- return hourly_snow_rate > threshold_snowfall
67+ covered = (hourly_snow_rate > threshold_snowfall )
68+ # no coverage when no snow on the ground
69+ if snow_depth is not None :
70+ covered = covered & (snow_depth >= threshold_depth )
71+ return covered
6072
6173
6274def coverage_nrel (snowfall , poa_irradiance , temp_air , surface_tilt ,
63- initial_coverage = 0 , threshold_snowfall = 1. ,
64- can_slide_coefficient = - 80. , slide_amount_coefficient = 0.197 ):
75+ snow_depth = None , initial_coverage = 0 , threshold_snowfall = 1. ,
76+ threshold_depth = 1. , can_slide_coefficient = - 80. ,
77+ slide_amount_coefficient = 0.197 ):
6578 '''
66- Calculates the fraction of the slant height of a row of modules covered by
67- snow at every time step.
79+ Calculates the fraction of the slant height of a row of modules that is
80+ covered by snow at every time step.
6881
6982 Implements the model described in [1]_ with minor improvements in [2]_,
7083 with the change that the output is in fraction of the row's slant height
@@ -74,20 +87,25 @@ def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt,
7487 Parameters
7588 ----------
7689 snowfall : Series
77- Accumulated snowfall within each time period. [cm]
90+ Snowfall within each time period. [cm]
7891 poa_irradiance : Series
7992 Total in-plane irradiance [W/m^2]
8093 temp_air : Series
8194 Ambient air temperature [C]
8295 surface_tilt : numeric
8396 Tilt of module's from horizontal, e.g. surface facing up = 0,
8497 surface facing horizon = 90. [degrees]
98+ snow_depth : Series, optional
99+ Snow depth on the ground at the beginning of each time period.
100+ Must have the same index as ``snowfall``. [cm]
85101 initial_coverage : float, default 0
86102 Fraction of row's slant height that is covered with snow at the
87103 beginning of the simulation. [unitless]
88- threshold_snowfall : float, default 1.0
89- Hourly snowfall above which snow coverage is set to the row's slant
90- height. [cm/hr]
104+ threshold_snowfall: float, default 1.0
105+ Hourly snowfall above which the row is fully covered for that hour.
106+ [cm/hr]
107+ threshold_depth: float, default 1.0
108+ Snow depth on the ground, above which snow can affect the modules. [cm]
91109 can_slide_coefficient : float, default -80.
92110 Coefficient to determine if snow can slide given irradiance and air
93111 temperature. [W/(m^2 C)]
@@ -103,8 +121,12 @@ def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt,
103121
104122 Notes
105123 -----
106- In [1]_, `can_slide_coefficient` is termed `m`, and the value of
107- `slide_amount_coefficient` is given in tenths of a module's slant height.
124+ In [1]_, ``can_slide_coefficient`` is termed `m`, and the value of
125+ ``slide_amount_coefficient`` is given in tenths of a module's slant height.
126+
127+ ``snow_depth`` is used to set ``snow_coverage`` to 0 (not fully covered)
128+ when snow is less than ``threshold_depth``. This check is described in
129+ [2]_ as needed for systems with low tilt angle.
108130
109131 References
110132 ----------
@@ -117,7 +139,8 @@ def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt,
117139 '''
118140
119141 # find times with new snowfall
120- new_snowfall = fully_covered_nrel (snowfall , threshold_snowfall )
142+ new_snowfall = fully_covered_nrel (snowfall , snow_depth , threshold_snowfall ,
143+ threshold_depth )
121144
122145 # set up output Series
123146 snow_coverage = pd .Series (np .nan , index = poa_irradiance .index )
@@ -132,6 +155,13 @@ def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt,
132155 # don't slide in the interval preceding the snowfall data
133156 slide_amt .iloc [0 ] = 0
134157
158+ if snow_depth is not None :
159+ # All slides off if snow on the ground is less than threshold_depth.
160+ # Described in [2] to avoid non-sliding snow for low-tilt systems.
161+ # Default threshold_depth of 1cm is from [2[ and SAM's implementation.
162+ # https://github.com/NREL/ssc/issues/1265
163+ slide_amt [snow_depth < threshold_depth ] = 1.
164+
135165 # build time series of cumulative slide amounts
136166 sliding_period_ID = new_snowfall .cumsum ()
137167 cumulative_sliding = slide_amt .groupby (sliding_period_ID ).cumsum ()
@@ -143,7 +173,6 @@ def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt,
143173 snow_coverage .ffill (inplace = True )
144174 snow_coverage -= cumulative_sliding
145175
146- # clean up periods where row is completely uncovered
147176 return snow_coverage .clip (lower = 0 )
148177
149178
0 commit comments