From 0b736d4121fc1d05e116dfd38cfa1a4a5a86ead1 Mon Sep 17 00:00:00 2001 From: Abhishek Parikh Date: Thu, 24 Jun 2021 21:19:50 -0700 Subject: [PATCH 1/8] first experimental commit --- pvlib/snow.py | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/pvlib/snow.py b/pvlib/snow.py index bf40c7b995..a3116f5a46 100644 --- a/pvlib/snow.py +++ b/pvlib/snow.py @@ -185,3 +185,71 @@ def dc_loss_nrel(snow_coverage, num_strings): Available at https://www.nrel.gov/docs/fy18osti/67399.pdf ''' return np.ceil(snow_coverage * num_strings) / num_strings + +def townsend_Se(S, N): + ''' + Calculates effective snow for a given month based upon the total snowfall + received in a month in inches and the number of events where snowfall is greater + than 1 inch + + Parameters + ---------- + S : numeric + Snowfall in inches received in a month + + N: numeric + Number of snowfall events with snowfall > 1" + + Returns + ------- + effective_snowfall : numeric + Effective snowfall as defined in the townsend model + + References + ---------- + .. [1] Townsend, Tim & Powers, Loren. (2011). Photovoltaics and snow: An + update from two winters of measurements in the SIERRA. Conference + Record of the IEEE Photovoltaic Specialists Conference. + 003231-003236. 10.1109/PVSC.2011.6186627. + Available at https://www.researchgate.net/publication/261042016_Photovoltaics_and_snow_An_update_from_two_winters_of_measurements_in_the_SIERRA + + ''' + return (0.5 * S * (1 + 1/N)) + +def townsend_snow_loss_model(x): + ''' + Loss, % = C1 x Se’ x cos2 + (tilt) x GIT x RH / TAIR2 + / POA0.67Eqn. 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] Townsend, Tim & Powers, Loren. (2011). Photovoltaics and snow: An + update from two winters of measurements in the SIERRA. Conference + Record of the IEEE Photovoltaic Specialists Conference. + 003231-003236. 10.1109/PVSC.2011.6186627. + Available at https://www.researchgate.net/publication/261042016_Photovoltaics_and_snow_An_update_from_two_winters_of_measurements_in_the_SIERRA + ''' + C1 = 5.7e04 + C2 = 0.51 + + + gamma = [R*Se’*cos(tilt)]/[(H2 – Se’2)/2*tan(P)] + + GIT = 1 - C2 * np.exp(-gamma) + loss = C1 * Se_ * (np.cos(tilt))**2 * GIT * RH / T_air**2 / POA**0.67 + + return x**2 From 3f2c40dfd98c0858e3f27169e36a4954ec7b39a6 Mon Sep 17 00:00:00 2001 From: Abhishek Parikh Date: Fri, 2 Jul 2021 09:16:35 -0700 Subject: [PATCH 2/8] Added numpy array support --- pvlib/snow.py | 63 ++++++++++++++++++++++++++++++---------- pvlib/tests/test_snow.py | 23 +++++++++++++++ 2 files changed, 71 insertions(+), 15 deletions(-) diff --git a/pvlib/snow.py b/pvlib/snow.py index a3116f5a46..e6f0fa1adc 100644 --- a/pvlib/snow.py +++ b/pvlib/snow.py @@ -214,26 +214,52 @@ def townsend_Se(S, N): Available at https://www.researchgate.net/publication/261042016_Photovoltaics_and_snow_An_update_from_two_winters_of_measurements_in_the_SIERRA ''' - return (0.5 * S * (1 + 1/N)) + return(np.where(N>0, 0.5 * S * (1 + 1/N), 0)) -def townsend_snow_loss_model(x): +def townsend_snow_loss_model(S, N, tilt, RH, T_air, POA, R, H, P=40): ''' - Loss, % = C1 x Se’ x cos2 - (tilt) x GIT x RH / TAIR2 - / POA0.67Eqn. 3 + Calculates monthly snow loss based on a generalized monthly snow loss model discussed in [1]_. Parameters ---------- - snow_coverage : numeric - The fraction of row slant height covered by snow at each time step. + S : numeric + Inches of snow received in the current month - num_strings: int - The number of parallel-connected strings along a row slant height. + N : numeric + Number of snowfall events with snowfall > 1" + + tilt : numeric + Array tilt in degrees + + RH : numeric + Relative humidity in percentage + + T_air : numeric + Ambient temperature in celcius + + POA : numeric + Plane of array irradiance in kWh/m2/month + + R : numeric + Row length in the slanted plane of array dimension in inches + + H : numeric + Drop height from array edge to ground in inches + + P : numeric + piled snow angle, assumed to stabilize at 40° , the midpoint of + 25°-55° avalanching slope angles + + S_prev : numeric + Inches of snow received in the previous month + + N_prev : numeric + Number of 1" or greater snow events in the previous month Returns ------- loss : numeric - fraction of DC capacity loss due to snow coverage at each time step. + Average monthly DC capacity loss in percentage due to snow coverage References ---------- @@ -243,13 +269,20 @@ def townsend_snow_loss_model(x): 003231-003236. 10.1109/PVSC.2011.6186627. Available at https://www.researchgate.net/publication/261042016_Photovoltaics_and_snow_An_update_from_two_winters_of_measurements_in_the_SIERRA ''' + C1 = 5.7e04 C2 = 0.51 - - - gamma = [R*Se’*cos(tilt)]/[(H2 – Se’2)/2*tan(P)] + + S_prev = np.roll(S,1) + N_prev = np.roll(N,1) + + Se = townsend_Se(S, N) + Se_prev = townsend_Se(S_prev, N_prev) + + Se_weighted = 1/3 * Se_prev + 2/3 * Se + gamma = (R * Se_weighted * np.cos(np.deg2rad(tilt)))/(np.clip((H**2 - Se_weighted**2),a_min=0.01,a_max=None)/2/np.tan(np.deg2rad(P))) GIT = 1 - C2 * np.exp(-gamma) - loss = C1 * Se_ * (np.cos(tilt))**2 * GIT * RH / T_air**2 / POA**0.67 + loss = C1 * Se_weighted * (np.cos(np.deg2rad(tilt)))**2 * GIT * RH / (T_air+273.15)**2 / POA**0.67 - return x**2 + return (np.round(loss,2)) \ No newline at end of file diff --git a/pvlib/tests/test_snow.py b/pvlib/tests/test_snow.py index 3db56c8f61..77c1f72399 100644 --- a/pvlib/tests/test_snow.py +++ b/pvlib/tests/test_snow.py @@ -95,3 +95,26 @@ def test_dc_loss_nrel(): expected = pd.Series([1, 1, .5, .625, .25, .5, 0]) actual = snow.dc_loss_nrel(snow_coverage, num_strings) assert_series_equal(expected, actual) + +def test_townsend_Se(): + S = np.array([10, 10, 5, 1, 0, 0, 0, 0, 0, 0, 5, 10]) + N = np.array([2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 2, 3]) + expected = np.array([7.5, 7.5, 5, 0, 0, 0, 0, 0, 0, 0, 3.75, 6.66666667]) + actual = snow.townsend_Se(S, N) + np.testing.assert_allclose(expected, actual, rtol=1e-07) + +def test_townsend_snow_loss_model(): + S = np.array([10, 10, 5, 1, 0, 0, 0, 0, 0, 0, 5, 10]) + N = np.array([2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 2, 3]) + tilt = 20 + RH = np.array([80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80]) + T_air = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) + POA = np.array([350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350]) + P = 40 + R = 100 + H = 10 + expected = np.array([7.7, 7.99, 6.22, 1.72, 0, 0, 0, 0, 0, 0, 2.64, 6.07]) + actual = snow.townsend_snow_loss_model(S,N,tilt,RH,T_air,POA,R, + H,P) + np.testing.assert_allclose(expected, actual, rtol=1e-07) + From 974d516d44dd508f2456e81334bc6a9c5b772087 Mon Sep 17 00:00:00 2001 From: Abhishek Parikh Date: Thu, 15 Jul 2021 20:16:35 -0700 Subject: [PATCH 3/8] changed var names --- pvlib/snow.py | 42 ++++++++++++++++++---------------------- pvlib/tests/test_snow.py | 18 ++++++++--------- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/pvlib/snow.py b/pvlib/snow.py index e6f0fa1adc..2d839c006c 100644 --- a/pvlib/snow.py +++ b/pvlib/snow.py @@ -216,46 +216,40 @@ def townsend_Se(S, N): ''' return(np.where(N>0, 0.5 * S * (1 + 1/N), 0)) -def townsend_snow_loss_model(S, N, tilt, RH, T_air, POA, R, H, P=40): +def loss_townsend(snow_total, snow_events, tilt, relative_humidity, temp_air, poa_global, row_len, H, P=40): ''' Calculates monthly snow loss based on a generalized monthly snow loss model discussed in [1]_. Parameters ---------- - S : numeric - Inches of snow received in the current month + snow_total : numeric + Inches of snow received in the current month. Referred as S in the paper - N : numeric - Number of snowfall events with snowfall > 1" + snow_events : numeric + Number of snowfall events with snowfall > 1". Referred as N in the paper tilt : numeric Array tilt in degrees - RH : numeric + relative_humidity : numeric Relative humidity in percentage - T_air : numeric + temp_air : numeric Ambient temperature in celcius - POA : numeric + poa_global : numeric Plane of array irradiance in kWh/m2/month - R : numeric + row_len : float Row length in the slanted plane of array dimension in inches - H : numeric + H : float Drop height from array edge to ground in inches - P : numeric + P : float piled snow angle, assumed to stabilize at 40° , the midpoint of 25°-55° avalanching slope angles - S_prev : numeric - Inches of snow received in the previous month - - N_prev : numeric - Number of 1" or greater snow events in the previous month - Returns ------- loss : numeric @@ -273,16 +267,18 @@ def townsend_snow_loss_model(S, N, tilt, RH, T_air, POA, R, H, P=40): C1 = 5.7e04 C2 = 0.51 - S_prev = np.roll(S,1) - N_prev = np.roll(N,1) + snow_total_prev = np.roll(snow_total,1) + snow_events_prev = np.roll(snow_events,1) - Se = townsend_Se(S, N) - Se_prev = townsend_Se(S_prev, N_prev) + Se = townsend_Se(snow_total, snow_events) + Se_prev = townsend_Se(snow_total_prev, snow_events_prev) Se_weighted = 1/3 * Se_prev + 2/3 * Se - gamma = (R * Se_weighted * np.cos(np.deg2rad(tilt)))/(np.clip((H**2 - Se_weighted**2),a_min=0.01,a_max=None)/2/np.tan(np.deg2rad(P))) + gamma = (row_len * Se_weighted * np.cos(np.deg2rad(tilt)))/ \ + (np.clip((H**2 - Se_weighted**2),a_min=0.01,a_max=None)/2/np.tan(np.deg2rad(P))) GIT = 1 - C2 * np.exp(-gamma) - loss = C1 * Se_weighted * (np.cos(np.deg2rad(tilt)))**2 * GIT * RH / (T_air+273.15)**2 / POA**0.67 + loss = C1 * Se_weighted * (np.cos(np.deg2rad(tilt)))**2 * GIT * relative_humidity / \ + (temp_air+273.15)**2 / poa_global**0.67 return (np.round(loss,2)) \ No newline at end of file diff --git a/pvlib/tests/test_snow.py b/pvlib/tests/test_snow.py index 77c1f72399..4aa30b62e8 100644 --- a/pvlib/tests/test_snow.py +++ b/pvlib/tests/test_snow.py @@ -103,18 +103,18 @@ def test_townsend_Se(): actual = snow.townsend_Se(S, N) np.testing.assert_allclose(expected, actual, rtol=1e-07) -def test_townsend_snow_loss_model(): - S = np.array([10, 10, 5, 1, 0, 0, 0, 0, 0, 0, 5, 10]) - N = np.array([2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 2, 3]) +def test_loss_townsend(): + snow_total = np.array([10, 10, 5, 1, 0, 0, 0, 0, 0, 0, 5, 10]) + snow_events = np.array([2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 2, 3]) tilt = 20 - RH = np.array([80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80]) - T_air = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) - POA = np.array([350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350]) + relative_humidity = np.array([80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80]) + temp_air = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) + poa_global = np.array([350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350]) P = 40 - R = 100 + row_len = 100 H = 10 expected = np.array([7.7, 7.99, 6.22, 1.72, 0, 0, 0, 0, 0, 0, 2.64, 6.07]) - actual = snow.townsend_snow_loss_model(S,N,tilt,RH,T_air,POA,R, - H,P) + actual = snow.loss_townsend(snow_total,snow_events,tilt,relative_humidity,temp_air, + poa_global,row_len,H,P) np.testing.assert_allclose(expected, actual, rtol=1e-07) From 655fa79574962e3a5cdd8897d3936fa3d6b04c4b Mon Sep 17 00:00:00 2001 From: Abhishek Parikh Date: Mon, 19 Jul 2021 17:06:29 -0700 Subject: [PATCH 4/8] changed townsend_Se to private --- docs/sphinx/source/api.rst | 1 + pvlib/snow.py | 25 ++++++++++++++----------- pvlib/tests/test_snow.py | 4 ++-- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/docs/sphinx/source/api.rst b/docs/sphinx/source/api.rst index 0e5438a7fc..6bf04af1af 100644 --- a/docs/sphinx/source/api.rst +++ b/docs/sphinx/source/api.rst @@ -403,6 +403,7 @@ Snow snow.coverage_nrel snow.fully_covered_nrel snow.dc_loss_nrel + snow.loss_townsend Soiling ------- diff --git a/pvlib/snow.py b/pvlib/snow.py index 2d839c006c..0efb62e006 100644 --- a/pvlib/snow.py +++ b/pvlib/snow.py @@ -186,11 +186,12 @@ def dc_loss_nrel(snow_coverage, num_strings): ''' return np.ceil(snow_coverage * num_strings) / num_strings -def townsend_Se(S, N): + +def _townsend_Se(S, N): ''' Calculates effective snow for a given month based upon the total snowfall - received in a month in inches and the number of events where snowfall is greater - than 1 inch + received in a month in inches and the number of events where snowfall is + greater than 1 inch Parameters ---------- @@ -210,12 +211,13 @@ def townsend_Se(S, N): .. [1] Townsend, Tim & Powers, Loren. (2011). Photovoltaics and snow: An update from two winters of measurements in the SIERRA. Conference Record of the IEEE Photovoltaic Specialists Conference. - 003231-003236. 10.1109/PVSC.2011.6186627. + 003231-003236. :doi:`10.1109/PVSC.2011.6186627` Available at https://www.researchgate.net/publication/261042016_Photovoltaics_and_snow_An_update_from_two_winters_of_measurements_in_the_SIERRA ''' return(np.where(N>0, 0.5 * S * (1 + 1/N), 0)) + def loss_townsend(snow_total, snow_events, tilt, relative_humidity, temp_air, poa_global, row_len, H, P=40): ''' Calculates monthly snow loss based on a generalized monthly snow loss model discussed in [1]_. @@ -235,10 +237,10 @@ def loss_townsend(snow_total, snow_events, tilt, relative_humidity, temp_air, po Relative humidity in percentage temp_air : numeric - Ambient temperature in celcius + Ambient temperature [C] poa_global : numeric - Plane of array irradiance in kWh/m2/month + Plane of array insolation in kWh/m2/month row_len : float Row length in the slanted plane of array dimension in inches @@ -270,15 +272,16 @@ def loss_townsend(snow_total, snow_events, tilt, relative_humidity, temp_air, po snow_total_prev = np.roll(snow_total,1) snow_events_prev = np.roll(snow_events,1) - Se = townsend_Se(snow_total, snow_events) - Se_prev = townsend_Se(snow_total_prev, snow_events_prev) + Se = _townsend_Se(snow_total, snow_events) + Se_prev = _townsend_Se(snow_total_prev, snow_events_prev) Se_weighted = 1/3 * Se_prev + 2/3 * Se gamma = (row_len * Se_weighted * np.cos(np.deg2rad(tilt)))/ \ - (np.clip((H**2 - Se_weighted**2),a_min=0.01,a_max=None)/2/np.tan(np.deg2rad(P))) + (np.clip((H**2 - Se_weighted**2),a_min=0.01,a_max=None)/2/ \ + np.tan(np.deg2rad(P))) GIT = 1 - C2 * np.exp(-gamma) - loss = C1 * Se_weighted * (np.cos(np.deg2rad(tilt)))**2 * GIT * relative_humidity / \ - (temp_air+273.15)**2 / poa_global**0.67 + loss = C1 * Se_weighted * (np.cos(np.deg2rad(tilt)))**2 * GIT * \ + relative_humidity / (temp_air+273.15)**2 / poa_global**0.67 return (np.round(loss,2)) \ No newline at end of file diff --git a/pvlib/tests/test_snow.py b/pvlib/tests/test_snow.py index 4aa30b62e8..cf7ad879b7 100644 --- a/pvlib/tests/test_snow.py +++ b/pvlib/tests/test_snow.py @@ -96,11 +96,11 @@ def test_dc_loss_nrel(): actual = snow.dc_loss_nrel(snow_coverage, num_strings) assert_series_equal(expected, actual) -def test_townsend_Se(): +def test__townsend_Se(): S = np.array([10, 10, 5, 1, 0, 0, 0, 0, 0, 0, 5, 10]) N = np.array([2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 2, 3]) expected = np.array([7.5, 7.5, 5, 0, 0, 0, 0, 0, 0, 0, 3.75, 6.66666667]) - actual = snow.townsend_Se(S, N) + actual = snow._townsend_Se(S, N) np.testing.assert_allclose(expected, actual, rtol=1e-07) def test_loss_townsend(): From c0c8b4d866d830b2ef0c1342c509846a0502443d Mon Sep 17 00:00:00 2001 From: abhisheksparikh Date: Wed, 16 Mar 2022 21:35:23 -0700 Subject: [PATCH 5/8] removed snow.loss_townsend in api.rst --- docs/sphinx/source/api.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/sphinx/source/api.rst b/docs/sphinx/source/api.rst index 6bf04af1af..0e5438a7fc 100644 --- a/docs/sphinx/source/api.rst +++ b/docs/sphinx/source/api.rst @@ -403,7 +403,6 @@ Snow snow.coverage_nrel snow.fully_covered_nrel snow.dc_loss_nrel - snow.loss_townsend Soiling ------- From 78716f7bfe504197df305e4cc7b7891933f4b075 Mon Sep 17 00:00:00 2001 From: abhisheksparikh Date: Wed, 16 Mar 2022 21:52:57 -0700 Subject: [PATCH 6/8] added loss_townsend description in effects_on_pv_system_output.rst --- docs/sphinx/source/reference/effects_on_pv_system_output.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/sphinx/source/reference/effects_on_pv_system_output.rst b/docs/sphinx/source/reference/effects_on_pv_system_output.rst index 92efa946b4..62c1035860 100644 --- a/docs/sphinx/source/reference/effects_on_pv_system_output.rst +++ b/docs/sphinx/source/reference/effects_on_pv_system_output.rst @@ -21,6 +21,7 @@ Snow snow.coverage_nrel snow.fully_covered_nrel snow.dc_loss_nrel + snow.loss_townsend Soiling ------- From 5e06a40f289fc098583c705875b07dddd29286b3 Mon Sep 17 00:00:00 2001 From: abhisheksparikh Date: Wed, 16 Mar 2022 22:13:15 -0700 Subject: [PATCH 7/8] fixed stickler checks --- pvlib/snow.py | 58 +++++++++++++++++++++------------------- pvlib/tests/test_snow.py | 14 ++++++---- 2 files changed, 40 insertions(+), 32 deletions(-) diff --git a/pvlib/snow.py b/pvlib/snow.py index 0efb62e006..db96bb1887 100644 --- a/pvlib/snow.py +++ b/pvlib/snow.py @@ -190,7 +190,7 @@ def dc_loss_nrel(snow_coverage, num_strings): def _townsend_Se(S, N): ''' Calculates effective snow for a given month based upon the total snowfall - received in a month in inches and the number of events where snowfall is + received in a month in inches and the number of events where snowfall is greater than 1 inch Parameters @@ -204,31 +204,35 @@ def _townsend_Se(S, N): Returns ------- effective_snowfall : numeric - Effective snowfall as defined in the townsend model + Effective snowfall as defined in the townsend model References ---------- - .. [1] Townsend, Tim & Powers, Loren. (2011). Photovoltaics and snow: An - update from two winters of measurements in the SIERRA. Conference - Record of the IEEE Photovoltaic Specialists Conference. + .. [1] Townsend, Tim & Powers, Loren. (2011). Photovoltaics and snow: An + update from two winters of measurements in the SIERRA. Conference + Record of the IEEE Photovoltaic Specialists Conference. 003231-003236. :doi:`10.1109/PVSC.2011.6186627` Available at https://www.researchgate.net/publication/261042016_Photovoltaics_and_snow_An_update_from_two_winters_of_measurements_in_the_SIERRA - ''' - return(np.where(N>0, 0.5 * S * (1 + 1/N), 0)) + '''# noqa + return(np.where(N > 0, 0.5 * S * (1 + 1/N), 0)) -def loss_townsend(snow_total, snow_events, tilt, relative_humidity, temp_air, poa_global, row_len, H, P=40): +def loss_townsend(snow_total, snow_events, tilt, relative_humidity, temp_air, + poa_global, row_len, H, P=40): ''' - Calculates monthly snow loss based on a generalized monthly snow loss model discussed in [1]_. + Calculates monthly snow loss based on a generalized monthly snow loss model + discussed in [1]_. Parameters ---------- snow_total : numeric - Inches of snow received in the current month. Referred as S in the paper + Inches of snow received in the current month. Referred as S in the + paper snow_events : numeric - Number of snowfall events with snowfall > 1". Referred as N in the paper + Number of snowfall events with snowfall > 1". Referred as N in the + paper tilt : numeric Array tilt in degrees @@ -249,7 +253,7 @@ def loss_townsend(snow_total, snow_events, tilt, relative_humidity, temp_air, po Drop height from array edge to ground in inches P : float - piled snow angle, assumed to stabilize at 40° , the midpoint of + piled snow angle, assumed to stabilize at 40° , the midpoint of 25°-55° avalanching slope angles Returns @@ -259,29 +263,29 @@ def loss_townsend(snow_total, snow_events, tilt, relative_humidity, temp_air, po References ---------- - .. [1] Townsend, Tim & Powers, Loren. (2011). Photovoltaics and snow: An - update from two winters of measurements in the SIERRA. Conference - Record of the IEEE Photovoltaic Specialists Conference. - 003231-003236. 10.1109/PVSC.2011.6186627. + .. [1] Townsend, Tim & Powers, Loren. (2011). Photovoltaics and snow: An + update from two winters of measurements in the SIERRA. Conference + Record of the IEEE Photovoltaic Specialists Conference. + 003231-003236. 10.1109/PVSC.2011.6186627. Available at https://www.researchgate.net/publication/261042016_Photovoltaics_and_snow_An_update_from_two_winters_of_measurements_in_the_SIERRA - ''' + '''# noqa C1 = 5.7e04 C2 = 0.51 - - snow_total_prev = np.roll(snow_total,1) - snow_events_prev = np.roll(snow_events,1) - + + snow_total_prev = np.roll(snow_total, 1) + snow_events_prev = np.roll(snow_events, 1) + Se = _townsend_Se(snow_total, snow_events) Se_prev = _townsend_Se(snow_total_prev, snow_events_prev) - + Se_weighted = 1/3 * Se_prev + 2/3 * Se - gamma = (row_len * Se_weighted * np.cos(np.deg2rad(tilt)))/ \ - (np.clip((H**2 - Se_weighted**2),a_min=0.01,a_max=None)/2/ \ + gamma = (row_len * Se_weighted * np.cos(np.deg2rad(tilt))) / \ + (np.clip((H**2 - Se_weighted**2), a_min=0.01, a_max=None) / 2 / np.tan(np.deg2rad(P))) - + GIT = 1 - C2 * np.exp(-gamma) loss = C1 * Se_weighted * (np.cos(np.deg2rad(tilt)))**2 * GIT * \ relative_humidity / (temp_air+273.15)**2 / poa_global**0.67 - - return (np.round(loss,2)) \ No newline at end of file + + return (np.round(loss, 2)) diff --git a/pvlib/tests/test_snow.py b/pvlib/tests/test_snow.py index cf7ad879b7..c580ef9d07 100644 --- a/pvlib/tests/test_snow.py +++ b/pvlib/tests/test_snow.py @@ -96,6 +96,7 @@ def test_dc_loss_nrel(): actual = snow.dc_loss_nrel(snow_coverage, num_strings) assert_series_equal(expected, actual) + def test__townsend_Se(): S = np.array([10, 10, 5, 1, 0, 0, 0, 0, 0, 0, 5, 10]) N = np.array([2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 2, 3]) @@ -103,18 +104,21 @@ def test__townsend_Se(): actual = snow._townsend_Se(S, N) np.testing.assert_allclose(expected, actual, rtol=1e-07) + def test_loss_townsend(): snow_total = np.array([10, 10, 5, 1, 0, 0, 0, 0, 0, 0, 5, 10]) snow_events = np.array([2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 2, 3]) tilt = 20 - relative_humidity = np.array([80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80]) + relative_humidity = np.array([80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80]) temp_air = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) - poa_global = np.array([350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350]) + poa_global = np.array([350, 350, 350, 350, 350, 350, 350, 350, 350, 350, + 350, 350]) P = 40 row_len = 100 H = 10 expected = np.array([7.7, 7.99, 6.22, 1.72, 0, 0, 0, 0, 0, 0, 2.64, 6.07]) - actual = snow.loss_townsend(snow_total,snow_events,tilt,relative_humidity,temp_air, - poa_global,row_len,H,P) + actual = snow.loss_townsend(snow_total, snow_events, tilt, + relative_humidity, temp_air, + poa_global, row_len, H, P) np.testing.assert_allclose(expected, actual, rtol=1e-07) - From a5c53151067e03327991732e226b22925901a89c Mon Sep 17 00:00:00 2001 From: abhisheksparikh Date: Fri, 18 Mar 2022 00:34:03 -0700 Subject: [PATCH 8/8] removed rounding of loss and changed to 0-1 range Several other small changes - variable names change, comment change - in response to Kevin's review notes --- docs/sphinx/source/whatsnew/v0.9.1.rst | 3 ++ pvlib/snow.py | 65 +++++++++++++------------- pvlib/tests/test_snow.py | 18 +++---- 3 files changed, 46 insertions(+), 40 deletions(-) diff --git a/docs/sphinx/source/whatsnew/v0.9.1.rst b/docs/sphinx/source/whatsnew/v0.9.1.rst index 8aa1abb71f..30ee76fb1a 100644 --- a/docs/sphinx/source/whatsnew/v0.9.1.rst +++ b/docs/sphinx/source/whatsnew/v0.9.1.rst @@ -26,6 +26,8 @@ Enhancements * Added ``map_variables`` option to :func:`~pvlib.iotools.read_crn` (:pull:`1368`) * Added :py:func:`pvlib.temperature.prilliman` for modeling cell temperature at short time steps (:issue:`1081`, :pull:`1391`) +* Added Townsend Powers Snow loss model in :py:func:`pvlib.snow` + (:issue:`1246`, :pull:`1251`) Bug fixes ~~~~~~~~~ @@ -78,3 +80,4 @@ Contributors * Saurabh Aneja (:ghuser:`spaneja`) * Jack Kelly (:ghuser:`JackKelly`) * Somasree Majumder(:ghuser:`soma2000-lang`) +* Abhishek Parikh (:ghuser:`abhisheksparikh`) diff --git a/pvlib/snow.py b/pvlib/snow.py index db96bb1887..c003c5531a 100644 --- a/pvlib/snow.py +++ b/pvlib/snow.py @@ -5,7 +5,7 @@ import numpy as np import pandas as pd -from pvlib.tools import sind +from pvlib.tools import sind, cosd, tand def _time_delta_in_hours(times): @@ -196,10 +196,10 @@ def _townsend_Se(S, N): Parameters ---------- S : numeric - Snowfall in inches received in a month + Snowfall in inches received in a month [in] N: numeric - Number of snowfall events with snowfall > 1" + Number of snowfall events with snowfall > 1" [-] Returns ------- @@ -209,17 +209,18 @@ def _townsend_Se(S, N): References ---------- .. [1] Townsend, Tim & Powers, Loren. (2011). Photovoltaics and snow: An - update from two winters of measurements in the SIERRA. Conference - Record of the IEEE Photovoltaic Specialists Conference. - 003231-003236. :doi:`10.1109/PVSC.2011.6186627` + update from two winters of measurements in the SIERRA. Conference + Record of the IEEE Photovoltaic Specialists Conference. + 003231-003236. :doi:`10.1109/PVSC.2011.6186627` Available at https://www.researchgate.net/publication/261042016_Photovoltaics_and_snow_An_update_from_two_winters_of_measurements_in_the_SIERRA - '''# noqa + ''' # noqa: E501 return(np.where(N > 0, 0.5 * S * (1 + 1/N), 0)) -def loss_townsend(snow_total, snow_events, tilt, relative_humidity, temp_air, - poa_global, row_len, H, P=40): +def loss_townsend(snow_total, snow_events, surface_tilt, relative_humidity, + temp_air, poa_global, slant_height, lower_edge_drop_height, + angle_of_repose=40): ''' Calculates monthly snow loss based on a generalized monthly snow loss model discussed in [1]_. @@ -228,47 +229,47 @@ def loss_townsend(snow_total, snow_events, tilt, relative_humidity, temp_air, ---------- snow_total : numeric Inches of snow received in the current month. Referred as S in the - paper + paper [in] snow_events : numeric Number of snowfall events with snowfall > 1". Referred as N in the - paper + paper [-] - tilt : numeric - Array tilt in degrees + surface_tilt : numeric + Array surface_tilt [deg] relative_humidity : numeric - Relative humidity in percentage + Relative humidity [%] temp_air : numeric - Ambient temperature [C] + Ambient temperature [°C] poa_global : numeric - Plane of array insolation in kWh/m2/month + Plane of array insolation [kWh/m2/month] - row_len : float - Row length in the slanted plane of array dimension in inches + slant_height : float + Row length in the slanted plane of array dimension [in] - H : float - Drop height from array edge to ground in inches + lower_edge_drop_height : float + Drop height from array edge to ground [in] P : float piled snow angle, assumed to stabilize at 40° , the midpoint of - 25°-55° avalanching slope angles + 25°-55° avalanching slope angles [deg] Returns ------- loss : numeric - Average monthly DC capacity loss in percentage due to snow coverage + Average monthly DC capacity loss due to snow coverage [%] References ---------- .. [1] Townsend, Tim & Powers, Loren. (2011). Photovoltaics and snow: An - update from two winters of measurements in the SIERRA. Conference - Record of the IEEE Photovoltaic Specialists Conference. - 003231-003236. 10.1109/PVSC.2011.6186627. + update from two winters of measurements in the SIERRA. Conference + Record of the IEEE Photovoltaic Specialists Conference. + 003231-003236. 10.1109/PVSC.2011.6186627. Available at https://www.researchgate.net/publication/261042016_Photovoltaics_and_snow_An_update_from_two_winters_of_measurements_in_the_SIERRA - '''# noqa + ''' # noqa: E501 C1 = 5.7e04 C2 = 0.51 @@ -280,12 +281,12 @@ def loss_townsend(snow_total, snow_events, tilt, relative_humidity, temp_air, Se_prev = _townsend_Se(snow_total_prev, snow_events_prev) Se_weighted = 1/3 * Se_prev + 2/3 * Se - gamma = (row_len * Se_weighted * np.cos(np.deg2rad(tilt))) / \ - (np.clip((H**2 - Se_weighted**2), a_min=0.01, a_max=None) / 2 / - np.tan(np.deg2rad(P))) + gamma = (slant_height * Se_weighted * cosd(surface_tilt)) / \ + (np.clip((lower_edge_drop_height**2 - Se_weighted**2), a_min=0.01, + a_max=None) / 2 / tand(angle_of_repose)) GIT = 1 - C2 * np.exp(-gamma) - loss = C1 * Se_weighted * (np.cos(np.deg2rad(tilt)))**2 * GIT * \ - relative_humidity / (temp_air+273.15)**2 / poa_global**0.67 + loss = (C1 * Se_weighted * (cosd(surface_tilt))**2 * GIT * + relative_humidity / (temp_air+273.15)**2 / poa_global**0.67) / 100 - return (np.round(loss, 2)) + return loss diff --git a/pvlib/tests/test_snow.py b/pvlib/tests/test_snow.py index c580ef9d07..bc71d5537a 100644 --- a/pvlib/tests/test_snow.py +++ b/pvlib/tests/test_snow.py @@ -108,17 +108,19 @@ def test__townsend_Se(): def test_loss_townsend(): snow_total = np.array([10, 10, 5, 1, 0, 0, 0, 0, 0, 0, 5, 10]) snow_events = np.array([2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 2, 3]) - tilt = 20 + surface_tilt = 20 relative_humidity = np.array([80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80]) temp_air = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) poa_global = np.array([350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350]) - P = 40 - row_len = 100 - H = 10 - expected = np.array([7.7, 7.99, 6.22, 1.72, 0, 0, 0, 0, 0, 0, 2.64, 6.07]) - actual = snow.loss_townsend(snow_total, snow_events, tilt, + angle_of_repose = 40 + slant_height = 100 + lower_edge_drop_height = 10 + expected = np.array([0.07696253, 0.07992262, 0.06216201, 0.01715392, 0, 0, + 0, 0, 0, 0, 0.02643821, 0.06068194]) + actual = snow.loss_townsend(snow_total, snow_events, surface_tilt, relative_humidity, temp_air, - poa_global, row_len, H, P) - np.testing.assert_allclose(expected, actual, rtol=1e-07) + poa_global, slant_height, + lower_edge_drop_height, angle_of_repose) + np.testing.assert_allclose(expected, actual, rtol=1e-05)