From 3594450987570e7cf524a67cf0aba4b93ff8dee7 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 6 Nov 2024 13:10:42 -0800 Subject: [PATCH 1/9] add snow_depth inputs --- pvlib/snow.py | 39 ++++++++++++++++++++++++++++++--------- pvlib/tests/test_snow.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 9 deletions(-) diff --git a/pvlib/snow.py b/pvlib/snow.py index 190fe7baae..2c51dc8256 100644 --- a/pvlib/snow.py +++ b/pvlib/snow.py @@ -13,23 +13,25 @@ def _time_delta_in_hours(times): return delta.dt.total_seconds().div(3600) -def fully_covered_nrel(snowfall, threshold_snowfall=1.): +def fully_covered_nrel(snowfall, snow_depth=None, 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 + snowfall: Series + Accumulated snowfall in each time period. [cm] + snow_depth: Series, optional + Snow depth on the ground at the beginning of each time period. + Must have the same index as `snowfall`. [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 + Series True where the snowfall exceeds the defined threshold to fully cover the panel. @@ -37,6 +39,10 @@ def fully_covered_nrel(snowfall, threshold_snowfall=1.): ----- Implements the model described in [1]_ with minor improvements in [2]_. + `snow_depth` is used to set coverage=0 when no snow is present on the + ground. This check is described in [2]_ as needed for systems with + low tilt angle. + References ---------- .. [1] Marion, B.; Schaefer, R.; Caine, H.; Sanchez, G. (2013). @@ -56,11 +62,15 @@ def fully_covered_nrel(snowfall, threshold_snowfall=1.): hourly_snow_rate.iloc[0] = snowfall.iloc[0] / timedelta else: # can't infer frequency from index hourly_snow_rate.iloc[0] = 0 # replaces NaN - return hourly_snow_rate > threshold_snowfall + covered = (hourly_snow_rate > threshold_snowfall) + # no coverage when no snow on the ground + if snow_depth is not None: + covered = covered & (snow_depth > 0.) + return covered def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt, - initial_coverage=0, threshold_snowfall=1., + snow_depth=None, 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 @@ -82,6 +92,9 @@ def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt, surface_tilt : numeric Tilt of module's from horizontal, e.g. surface facing up = 0, surface facing horizon = 90. [degrees] + snow_depth : Series, optional + Snow depth on the ground at the beginning of each time period. + Must have the same index as `snowfall`. [cm] initial_coverage : float, default 0 Fraction of row's slant height that is covered with snow at the beginning of the simulation. [unitless] @@ -106,6 +119,10 @@ def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt, 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. + `snow_depth` is used to set coverage=0 when no snow is present on the + ground. This check is described in [2]_ as needed for systems with + low tilt angle. + References ---------- .. [1] Marion, B.; Schaefer, R.; Caine, H.; Sanchez, G. (2013). @@ -117,7 +134,7 @@ def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt, ''' # find times with new snowfall - new_snowfall = fully_covered_nrel(snowfall, threshold_snowfall) + new_snowfall = fully_covered_nrel(snowfall, snow_depth, threshold_snowfall) # set up output Series snow_coverage = pd.Series(np.nan, index=poa_irradiance.index) @@ -143,6 +160,10 @@ def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt, snow_coverage.ffill(inplace=True) snow_coverage -= cumulative_sliding + if snow_depth is not None: + # no coverage when there's no snow on the ground + # described in [2] to avoid non-sliding snow for low-tilt systems. + snow_coverage[snow_depth<=0] = 0. # clean up periods where row is completely uncovered return snow_coverage.clip(lower=0) diff --git a/pvlib/tests/test_snow.py b/pvlib/tests/test_snow.py index 19e79b5179..042350f0f6 100644 --- a/pvlib/tests/test_snow.py +++ b/pvlib/tests/test_snow.py @@ -19,6 +19,18 @@ def test_fully_covered_nrel(): assert_series_equal(expected, fully_covered) +def test_fully_covered_nrel_with_snow_depth(): + dt = pd.date_range(start="2019-1-1 12:00:00", end="2019-1-1 18:00:00", + freq='1h') + snowfall_data = pd.Series([1, 5, .6, 4, .23, -5, 19], index=dt) + snow_depth = pd.Series([0., 1, 6, 6.6, 10.6, 10., -2], index=dt) + expected = pd.Series([False, True, False, True, False, False, False], + index=dt) + fully_covered = snow.fully_covered_nrel(snowfall_data, + snow_depth=snow_depth) + assert_series_equal(expected, fully_covered) + + def test_coverage_nrel_hourly(): surface_tilt = 45 slide_amount_coefficient = 0.197 @@ -38,6 +50,27 @@ def test_coverage_nrel_hourly(): assert_series_equal(expected, snow_coverage) +def test_coverage_nrel_hourly_with_snow_depth(): + surface_tilt = 45 + slide_amount_coefficient = 0.197 + dt = pd.date_range(start="2019-1-1 10:00:00", end="2019-1-1 17:00:00", + freq='1h') + poa_irradiance = pd.Series([400, 200, 100, 1234, 134, 982, 100, 100], + index=dt) + temp_air = pd.Series([10, 2, 10, 1234, 34, 982, 10, 10], index=dt) + snowfall_data = pd.Series([1, .5, .6, .4, .23, -5, .1, .1], index=dt) + snow_depth = pd.Series([1, 1, 1, 1, 0, 1, 0, .1], index=dt) + snow_coverage = snow.coverage_nrel( + snowfall_data, poa_irradiance, temp_air, surface_tilt, + snow_depth=snow_depth, threshold_snowfall=0.6) + + slide_amt = slide_amount_coefficient * sind(surface_tilt) + covered = 1.0 - slide_amt * np.array([0, 1, 2, 3, 4, 5, 6, 7]) + expected = pd.Series(covered, index=dt) + expected[snow_depth<=0] = 0 + assert_series_equal(expected, snow_coverage) + + def test_coverage_nrel_subhourly(): surface_tilt = 45 slide_amount_coefficient = 0.197 From 9716bc8b36ed446f50cc11514590584a96414872 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 6 Nov 2024 13:37:57 -0800 Subject: [PATCH 2/9] linter --- pvlib/snow.py | 2 +- pvlib/tests/test_snow.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pvlib/snow.py b/pvlib/snow.py index 2c51dc8256..c98739712d 100644 --- a/pvlib/snow.py +++ b/pvlib/snow.py @@ -163,7 +163,7 @@ def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt, if snow_depth is not None: # no coverage when there's no snow on the ground # described in [2] to avoid non-sliding snow for low-tilt systems. - snow_coverage[snow_depth<=0] = 0. + snow_coverage[snow_depth <= 0] = 0. # clean up periods where row is completely uncovered return snow_coverage.clip(lower=0) diff --git a/pvlib/tests/test_snow.py b/pvlib/tests/test_snow.py index 042350f0f6..da1e1f1f6c 100644 --- a/pvlib/tests/test_snow.py +++ b/pvlib/tests/test_snow.py @@ -67,7 +67,7 @@ def test_coverage_nrel_hourly_with_snow_depth(): slide_amt = slide_amount_coefficient * sind(surface_tilt) covered = 1.0 - slide_amt * np.array([0, 1, 2, 3, 4, 5, 6, 7]) expected = pd.Series(covered, index=dt) - expected[snow_depth<=0] = 0 + expected[snow_depth <= 0] = 0 assert_series_equal(expected, snow_coverage) From cd579f2f432f7ef880a1790da09d9c25aa74d54a Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 6 Nov 2024 13:40:01 -0800 Subject: [PATCH 3/9] double ticks --- pvlib/snow.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pvlib/snow.py b/pvlib/snow.py index c98739712d..0fef53de6d 100644 --- a/pvlib/snow.py +++ b/pvlib/snow.py @@ -24,7 +24,7 @@ def fully_covered_nrel(snowfall, snow_depth=None, threshold_snowfall=1.): Accumulated snowfall in each time period. [cm] snow_depth: Series, optional Snow depth on the ground at the beginning of each time period. - Must have the same index as `snowfall`. [cm] + Must have the same index as ``snowfall``. [cm] threshold_snowfall: float, default 1.0 Hourly snowfall above which snow coverage is set to the row's slant height. [cm/hr] @@ -39,7 +39,7 @@ def fully_covered_nrel(snowfall, snow_depth=None, threshold_snowfall=1.): ----- Implements the model described in [1]_ with minor improvements in [2]_. - `snow_depth` is used to set coverage=0 when no snow is present on the + ``snow_depth`` is used to set coverage=0 when no snow is present on the ground. This check is described in [2]_ as needed for systems with low tilt angle. @@ -94,7 +94,7 @@ def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt, surface facing horizon = 90. [degrees] snow_depth : Series, optional Snow depth on the ground at the beginning of each time period. - Must have the same index as `snowfall`. [cm] + Must have the same index as ``snowfall``. [cm] initial_coverage : float, default 0 Fraction of row's slant height that is covered with snow at the beginning of the simulation. [unitless] @@ -116,10 +116,10 @@ def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt, 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. + 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. - `snow_depth` is used to set coverage=0 when no snow is present on the + ``snow_depth`` is used to set coverage=0 when no snow is present on the ground. This check is described in [2]_ as needed for systems with low tilt angle. From 7194e3cf5e59a132ee13c504a1d93cc2185f28ae Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 11 Dec 2024 09:20:05 -0700 Subject: [PATCH 4/9] review comments, improve some descriptions --- pvlib/snow.py | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/pvlib/snow.py b/pvlib/snow.py index 0fef53de6d..75d6e33535 100644 --- a/pvlib/snow.py +++ b/pvlib/snow.py @@ -15,8 +15,7 @@ def _time_delta_in_hours(times): def fully_covered_nrel(snowfall, snow_depth=None, threshold_snowfall=1.): ''' - Calculates the timesteps when the row's slant height is fully covered - by snow. + Calculates the timesteps when modules are fully covered by snow. Parameters ---------- @@ -26,22 +25,22 @@ def fully_covered_nrel(snowfall, snow_depth=None, threshold_snowfall=1.): Snow depth on the ground at the beginning of each time period. Must have the same index as ``snowfall``. [cm] threshold_snowfall: float, default 1.0 - Hourly snowfall above which snow coverage is set to the row's slant - height. [cm/hr] + Hourly snowfall above which the row is fully covered for that hour. + [cm/hr] Returns ---------- - Series - True where the snowfall exceeds the defined threshold to fully cover - the panel. + covered: Series + A Series of boolean, 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]_. - ``snow_depth`` is used to set coverage=0 when no snow is present on the - ground. This check is described in [2]_ as needed for systems with - low tilt angle. + ``snow_depth`` is used to return `False` (not fully covered) when no snow + is present on the ground. This check is described in [2]_ as needed for + systems with low tilt angle. References ---------- @@ -73,8 +72,8 @@ def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt, snow_depth=None, 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. + Calculates the fraction of the slant height of a row of modules that is + 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 @@ -119,9 +118,9 @@ def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt, 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. - ``snow_depth`` is used to set coverage=0 when no snow is present on the - ground. This check is described in [2]_ as needed for systems with - low tilt angle. + ``snow_depth`` is used to set ``snow_coverage`` to 0 when no snow is + present on the ground. This check is described in [2]_ as needed for + systems with low tilt angle. References ---------- From 11cf12e8f8469ad3cfe91a8c62d6dd00b2acda36 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 11 Dec 2024 09:27:34 -0700 Subject: [PATCH 5/9] whatsnew --- docs/sphinx/source/whatsnew/v0.11.2.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/sphinx/source/whatsnew/v0.11.2.rst b/docs/sphinx/source/whatsnew/v0.11.2.rst index 087697f778..beb5bafaff 100644 --- a/docs/sphinx/source/whatsnew/v0.11.2.rst +++ b/docs/sphinx/source/whatsnew/v0.11.2.rst @@ -22,6 +22,9 @@ Bug Fixes (:issue:`1338`, :pull:`2227`) * Handle DST transitions that happen at midnight in :py:func:`pvlib.solarposition.hour_angle` (:issue:`2132` :pull:`2133`) +* Add a check to :py:func:`~pvlib.snow.fully_covered_nrel` and + :py:func:`~pvlib.snow.coverage_nrel`. The check uses snow depth on the ground + to improve modeling for systems with shallow tilt angles. (:issue:`1171`, :pull:`2292`) Bug fixes ~~~~~~~~~ From b6424e3e462d7fd6335a18124aff1aa6a01351a9 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 20 Dec 2024 15:24:09 -0700 Subject: [PATCH 6/9] clarify threshold_depth --- pvlib/snow.py | 33 ++++++++++++++++++++------------- pvlib/tests/test_snow.py | 9 ++++++--- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/pvlib/snow.py b/pvlib/snow.py index 75d6e33535..9f4234caf0 100644 --- a/pvlib/snow.py +++ b/pvlib/snow.py @@ -13,20 +13,23 @@ def _time_delta_in_hours(times): return delta.dt.total_seconds().div(3600) -def fully_covered_nrel(snowfall, snow_depth=None, threshold_snowfall=1.): +def fully_covered_nrel(snowfall, snow_depth=None, threshold_snowfall=1., + threshold_depth=1.): ''' Calculates the timesteps when modules are fully covered by snow. Parameters ---------- snowfall: Series - Accumulated snowfall in each time period. [cm] + Snowfall in each time period. [cm] snow_depth: Series, optional Snow depth on the ground at the beginning of each time period. Must have the same index as ``snowfall``. [cm] threshold_snowfall: float, default 1.0 Hourly snowfall above which the row is fully covered for that hour. [cm/hr] + threshold_depth: float, default 1.0 + Snow depth on the ground, above which snow can affect the modules. [cm] Returns ---------- @@ -38,9 +41,9 @@ def fully_covered_nrel(snowfall, snow_depth=None, threshold_snowfall=1.): ----- Implements the model described in [1]_ with minor improvements in [2]_. - ``snow_depth`` is used to return `False` (not fully covered) when no snow - is present on the ground. This check is described in [2]_ as needed for - systems with low tilt angle. + ``snow_depth`` is used to return `False` (not fully covered) when snow + is less than ``threshold_depth. This check is described in [2]_ as needed + for systems with low tilt angle. References ---------- @@ -64,13 +67,14 @@ def fully_covered_nrel(snowfall, snow_depth=None, threshold_snowfall=1.): covered = (hourly_snow_rate > threshold_snowfall) # no coverage when no snow on the ground if snow_depth is not None: - covered = covered & (snow_depth > 0.) + covered = covered & (snow_depth >= threshold_depth) return covered def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt, snow_depth=None, initial_coverage=0, threshold_snowfall=1., - can_slide_coefficient=-80., slide_amount_coefficient=0.197): + threshold_depth=1., can_slide_coefficient=-80., + slide_amount_coefficient=0.197): ''' Calculates the fraction of the slant height of a row of modules that is covered by snow at every time step. @@ -83,7 +87,7 @@ def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt, Parameters ---------- snowfall : Series - Accumulated snowfall within each time period. [cm] + Snowfall within each time period. [cm] poa_irradiance : Series Total in-plane irradiance [W/m^2] temp_air : Series @@ -100,6 +104,8 @@ def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt, threshold_snowfall : float, default 1.0 Hourly snowfall above which snow coverage is set to the row's slant height. [cm/hr] + threshold_depth: float, default 1.0 + Snow depth on the ground, above which snow can affect the modules. [cm] can_slide_coefficient : float, default -80. Coefficient to determine if snow can slide given irradiance and air temperature. [W/(m^2 C)] @@ -118,9 +124,9 @@ def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt, 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. - ``snow_depth`` is used to set ``snow_coverage`` to 0 when no snow is - present on the ground. This check is described in [2]_ as needed for - systems with low tilt angle. + ``snow_depth`` is used to set ``snow_coverage`` to 0 (not fully covered) + when snow is less than ``threshold_depth. . This check is described in + [2]_ as needed for systems with low tilt angle. References ---------- @@ -133,7 +139,8 @@ def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt, ''' # find times with new snowfall - new_snowfall = fully_covered_nrel(snowfall, snow_depth, threshold_snowfall) + new_snowfall = fully_covered_nrel(snowfall, snow_depth, threshold_snowfall, + threshold_depth) # set up output Series snow_coverage = pd.Series(np.nan, index=poa_irradiance.index) @@ -162,7 +169,7 @@ def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt, if snow_depth is not None: # no coverage when there's no snow on the ground # described in [2] to avoid non-sliding snow for low-tilt systems. - snow_coverage[snow_depth <= 0] = 0. + snow_coverage[snow_depth < threshold_depth] = 0. # clean up periods where row is completely uncovered return snow_coverage.clip(lower=0) diff --git a/pvlib/tests/test_snow.py b/pvlib/tests/test_snow.py index da1e1f1f6c..65687f3f0b 100644 --- a/pvlib/tests/test_snow.py +++ b/pvlib/tests/test_snow.py @@ -27,7 +27,8 @@ def test_fully_covered_nrel_with_snow_depth(): expected = pd.Series([False, True, False, True, False, False, False], index=dt) fully_covered = snow.fully_covered_nrel(snowfall_data, - snow_depth=snow_depth) + snow_depth=snow_depth, + threshold_depth=0.) assert_series_equal(expected, fully_covered) @@ -53,6 +54,7 @@ def test_coverage_nrel_hourly(): def test_coverage_nrel_hourly_with_snow_depth(): surface_tilt = 45 slide_amount_coefficient = 0.197 + threshold_depth = 0.5 dt = pd.date_range(start="2019-1-1 10:00:00", end="2019-1-1 17:00:00", freq='1h') poa_irradiance = pd.Series([400, 200, 100, 1234, 134, 982, 100, 100], @@ -62,12 +64,13 @@ def test_coverage_nrel_hourly_with_snow_depth(): snow_depth = pd.Series([1, 1, 1, 1, 0, 1, 0, .1], index=dt) snow_coverage = snow.coverage_nrel( snowfall_data, poa_irradiance, temp_air, surface_tilt, - snow_depth=snow_depth, threshold_snowfall=0.6) + snow_depth=snow_depth, threshold_snowfall=0.6, + threshold_depth=threshold_depth) slide_amt = slide_amount_coefficient * sind(surface_tilt) covered = 1.0 - slide_amt * np.array([0, 1, 2, 3, 4, 5, 6, 7]) expected = pd.Series(covered, index=dt) - expected[snow_depth <= 0] = 0 + expected[snow_depth < threshold_depth] = 0 assert_series_equal(expected, snow_coverage) From d46837cabfd862f74baa8c26ba9e3727e88f65b9 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 20 Dec 2024 15:26:21 -0700 Subject: [PATCH 7/9] add note about default value --- pvlib/snow.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pvlib/snow.py b/pvlib/snow.py index 9f4234caf0..4927b326d0 100644 --- a/pvlib/snow.py +++ b/pvlib/snow.py @@ -169,6 +169,9 @@ def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt, if snow_depth is not None: # no coverage when there's no snow on the ground # described in [2] to avoid non-sliding snow for low-tilt systems. + # default threshold_depth of 1cm is from SAM's implementation and + # is different than the value of 0cm implied in [2]. + # https://github.com.mcas-gov.ms/NREL/ssc/issues/1265 snow_coverage[snow_depth < threshold_depth] = 0. # clean up periods where row is completely uncovered return snow_coverage.clip(lower=0) From 57f5eefa968866c6a455182b2b31ef8ad141213f Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Sun, 22 Dec 2024 11:15:29 -0700 Subject: [PATCH 8/9] move check for snow depth --- pvlib/snow.py | 22 +++++++++++++++------- pvlib/tests/test_snow.py | 13 +++++++------ 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/pvlib/snow.py b/pvlib/snow.py index 4927b326d0..aab63fdf62 100644 --- a/pvlib/snow.py +++ b/pvlib/snow.py @@ -155,6 +155,14 @@ def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt, # don't slide in the interval preceding the snowfall data slide_amt.iloc[0] = 0 + if snow_depth is not None: + # all slides off if there is no snow on the ground + # described in [2] to avoid non-sliding snow for low-tilt systems. + # default threshold_depth of 1cm is from SAM's implementation and + # is different than the value of 0cm implied in [2]. + # https://github.com.mcas-gov.ms/NREL/ssc/issues/1265 + slide_amt[snow_depth < threshold_depth] = 1. + # build time series of cumulative slide amounts sliding_period_ID = new_snowfall.cumsum() cumulative_sliding = slide_amt.groupby(sliding_period_ID).cumsum() @@ -166,13 +174,13 @@ def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt, snow_coverage.ffill(inplace=True) snow_coverage -= cumulative_sliding - if snow_depth is not None: - # no coverage when there's no snow on the ground - # described in [2] to avoid non-sliding snow for low-tilt systems. - # default threshold_depth of 1cm is from SAM's implementation and - # is different than the value of 0cm implied in [2]. - # https://github.com.mcas-gov.ms/NREL/ssc/issues/1265 - snow_coverage[snow_depth < threshold_depth] = 0. + # if snow_depth is not None: + # # no coverage when there's no snow on the ground + # # described in [2] to avoid non-sliding snow for low-tilt systems. + # # default threshold_depth of 1cm is from SAM's implementation and + # # is different than the value of 0cm implied in [2]. + # # https://github.com.mcas-gov.ms/NREL/ssc/issues/1265 + # snow_coverage[snow_depth < threshold_depth] = 0. # clean up periods where row is completely uncovered return snow_coverage.clip(lower=0) diff --git a/pvlib/tests/test_snow.py b/pvlib/tests/test_snow.py index 65687f3f0b..6d8954011e 100644 --- a/pvlib/tests/test_snow.py +++ b/pvlib/tests/test_snow.py @@ -55,20 +55,21 @@ def test_coverage_nrel_hourly_with_snow_depth(): surface_tilt = 45 slide_amount_coefficient = 0.197 threshold_depth = 0.5 - dt = pd.date_range(start="2019-1-1 10:00:00", end="2019-1-1 17:00:00", + dt = pd.date_range(start="2019-1-1 10:00:00", end="2019-1-1 18:00:00", freq='1h') - poa_irradiance = pd.Series([400, 200, 100, 1234, 134, 982, 100, 100], + poa_irradiance = pd.Series([400, 200, 100, 1234, 134, 982, 100, 100, 100], index=dt) - temp_air = pd.Series([10, 2, 10, 1234, 34, 982, 10, 10], index=dt) - snowfall_data = pd.Series([1, .5, .6, .4, .23, -5, .1, .1], index=dt) - snow_depth = pd.Series([1, 1, 1, 1, 0, 1, 0, .1], index=dt) + temp_air = pd.Series([10, 2, 10, 1234, 34, 982, 10, 10, 10], index=dt) + # restarts with new snow on 5th time step + snowfall_data = pd.Series([1, .5, .6, .4, .23, 5., .1, .1, 0.], index=dt) + snow_depth = pd.Series([1, 1, 1, 1, 0, 1, 1, 0, .1], index=dt) snow_coverage = snow.coverage_nrel( snowfall_data, poa_irradiance, temp_air, surface_tilt, snow_depth=snow_depth, threshold_snowfall=0.6, threshold_depth=threshold_depth) slide_amt = slide_amount_coefficient * sind(surface_tilt) - covered = 1.0 - slide_amt * np.array([0, 1, 2, 3, 4, 5, 6, 7]) + covered = 1.0 - slide_amt * np.array([0, 1, 2, 3, 0, 0, 1, 0, 0]) expected = pd.Series(covered, index=dt) expected[snow_depth < threshold_depth] = 0 assert_series_equal(expected, snow_coverage) From 81c44d70a45c7e3ece5c091af482fd9baa337dfe Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Sun, 22 Dec 2024 11:45:54 -0700 Subject: [PATCH 9/9] comments --- pvlib/snow.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pvlib/snow.py b/pvlib/snow.py index aab63fdf62..580489c575 100644 --- a/pvlib/snow.py +++ b/pvlib/snow.py @@ -174,14 +174,6 @@ def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt, snow_coverage.ffill(inplace=True) snow_coverage -= cumulative_sliding - # if snow_depth is not None: - # # no coverage when there's no snow on the ground - # # described in [2] to avoid non-sliding snow for low-tilt systems. - # # default threshold_depth of 1cm is from SAM's implementation and - # # is different than the value of 0cm implied in [2]. - # # https://github.com.mcas-gov.ms/NREL/ssc/issues/1265 - # snow_coverage[snow_depth < threshold_depth] = 0. - # clean up periods where row is completely uncovered return snow_coverage.clip(lower=0)