Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion pvlib/snow.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,8 @@

def loss_townsend(snow_total, snow_events, surface_tilt, relative_humidity,
temp_air, poa_global, slant_height, lower_edge_height,
string_factor=1.0, angle_of_repose=40):
string_factor=1.0, angle_of_repose=40,
front_side_fraction=None):
'''
Calculates monthly snow loss based on the Townsend monthly snow loss
model.
Expand Down Expand Up @@ -293,6 +294,13 @@
Piled snow angle, assumed to stabilize at 40°, the midpoint of
25°-55° avalanching slope angles. [deg]

front_side_fraction : numeric or array-like, default None
Optional multiplier applied to the calculated loss fraction. For
bifacial systems, this can be used to scale the snow loss by the
front-side energy fraction from a no-soiling simulation. For example,
use 0.9 when 90% of monthly energy is from the front side and 10% is
from the rear side. If None, no bifacial adjustment is applied. [-]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
front_side_fraction : numeric or array-like, default None
Optional multiplier applied to the calculated loss fraction. For
bifacial systems, this can be used to scale the snow loss by the
front-side energy fraction from a no-soiling simulation. For example,
use 0.9 when 90% of monthly energy is from the front side and 10% is
from the rear side. If None, no bifacial adjustment is applied. [-]
front_side_fraction : numeric or array-like, default 1.0
Fraction of monthly energy from front-side insolation (unitless).
Multiplies the calculated loss fraction. For example,
use 0.9 when 90% of monthly energy is from the front side
of a bifacial system and 10% is from the rear side.

Here I think 1.0 is a suitable default.


Check failure on line 303 in pvlib/snow.py

View workflow job for this annotation

GitHub Actions / flake8-linter

W293 blank line contains whitespace
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deleting this blank space will resolve the flake8 error (same below)

Suggested change

Returns
-------
loss : array-like
Expand All @@ -310,6 +318,10 @@
publication of [1]_, as described in [2]_.
The definition for snow events documented above is based on [3]_.

For bifacial systems, [2]_ recommends including both front-side and
rear-side insolation in ``poa_global``. The resulting loss may then be
scaled by the front-side energy fraction using ``front_side_fraction``.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
rear-side insolation in ``poa_global``. The resulting loss may then be
scaled by the front-side energy fraction using ``front_side_fraction``.
rear-side insolation in ``poa_global``. The resulting loss is
scaled by the front-side energy fraction ``front_side_fraction``.


References
----------
.. [1] Townsend, Tim & Powers, Loren. (2011). Photovoltaics and snow: An
Expand Down Expand Up @@ -384,4 +396,8 @@
* string_factor
)

if front_side_fraction is not None:
front_side_fraction = np.asarray(front_side_fraction)
Copy link
Copy Markdown
Member

@cwhanse cwhanse May 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this if and np.asarray are unnecessary if the default is set to 1.0

loss_fraction = loss_fraction * front_side_fraction

return np.clip(loss_fraction, 0, 1)
26 changes: 26 additions & 0 deletions tests/test_snow.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,3 +247,29 @@
poa_global, slant_height, lower_edge_height, string_factor)
actual = np.around(actual * 100)
assert np.allclose(expected, actual)


Check failure on line 251 in tests/test_snow.py

View workflow job for this annotation

GitHub Actions / flake8-linter

W293 blank line contains whitespace
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deleting this blank space will resolve the flake8 error

Suggested change

def test_loss_townsend_front_side_fraction():
snow_total = np.array([25.4, 25.4, 12.7, 2.54, 0, 0, 0, 0, 0, 0, 12.7,
25.4])
snow_events = np.array([2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 2, 3])
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([350000, 350000, 350000, 350000, 350000, 350000,
350000, 350000, 350000, 350000, 350000, 350000])
Comment thread
shethkajal7 marked this conversation as resolved.
Outdated
slant_height = 2.54
lower_edge_height = 0.254
front_side_fraction = 0.9

unadjusted = snow.loss_townsend(
snow_total, snow_events, surface_tilt, relative_humidity, temp_air,
poa_global, slant_height, lower_edge_height)

adjusted = snow.loss_townsend(
snow_total, snow_events, surface_tilt, relative_humidity, temp_air,
poa_global, slant_height, lower_edge_height,
front_side_fraction=front_side_fraction)

np.testing.assert_allclose(adjusted, unadjusted * front_side_fraction)

Check failure on line 275 in tests/test_snow.py

View workflow job for this annotation

GitHub Actions / flake8-linter

W292 no newline at end of file
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a new line at the end of the file will resolve this flake8 error

Suggested change
np.testing.assert_allclose(adjusted, unadjusted * front_side_fraction)
np.testing.assert_allclose(adjusted, unadjusted * front_side_fraction)

Loading