Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Snow coverage model from SAM #764

Merged
merged 163 commits into from
Mar 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
163 commits
Select commit Hold shift + click to select a range
bea249f
added snow_model code. Needs formatting
JPalakapillyKWH Aug 8, 2019
a931096
starting numpy-ification
JPalakapillyKWH Aug 8, 2019
ca285d7
minor changes
JPalakapillyKWH Aug 9, 2019
f7acc74
added tests and made model faster
JPalakapillyKWH Aug 13, 2019
9a7964b
finished tests.
JPalakapillyKWH Aug 13, 2019
67cff8b
fixed lint errors.
JPalakapillyKWH Aug 13, 2019
97565bf
fixed minor lint error
JPalakapillyKWH Aug 14, 2019
78ac66a
fixed bug where model stopped prematurely
JPalakapillyKWH Aug 15, 2019
a2ccd92
docstring changes mostly. Changed the location of division by 10 for …
JPalakapillyKWH Aug 16, 2019
4d65294
fixed tests to account for prev change
JPalakapillyKWH Aug 16, 2019
bcd8c37
fixed docstring for snow slide amount
JPalakapillyKWH Aug 16, 2019
8edfffe
docstring edits, move some functions to private
cwhanse Feb 7, 2020
cf71e2a
Merge branch 'master' of https://github.com/pvlib/pvlib-python into s…
cwhanse Feb 7, 2020
43d21cd
rewrite, reduce helpers, remove while loop
cwhanse Feb 13, 2020
1b5dcb1
rename functions, review responses, add subhourly test
cwhanse Feb 14, 2020
7b7edfe
fixes
cwhanse Feb 14, 2020
c5ac03b
temperature -> temp_air, test correction, fix default for m
cwhanse Feb 14, 2020
b9f1988
put m=-80 back
cwhanse Feb 14, 2020
04a0b15
edits for threshold, use first data point
cwhanse Mar 2, 2020
109fa7d
add initial snow, better logic for coverage events
cwhanse Mar 4, 2020
436c8d1
lint, text fix
cwhanse Mar 4, 2020
92d693c
workaround for py35/pandas 0.23.4
cwhanse Mar 5, 2020
b5a9fb9
move line
cwhanse Mar 5, 2020
7116cd2
improve comments
cwhanse Mar 6, 2020
54f7a5e
improvements from review
cwhanse Mar 6, 2020
63c2f58
improve docstring
cwhanse Mar 6, 2020
c766e9a
reorder comparison, add missing inplace
cwhanse Mar 6, 2020
4c25f2a
correct test, cut/paste error
cwhanse Mar 6, 2020
6c6c663
vectorize
cwhanse Mar 9, 2020
7689ef6
correct .clip
cwhanse Mar 9, 2020
47c26da
function and file renaming
cwhanse Mar 10, 2020
ff2ca66
Merge branch 'master' of https://github.com/pvlib/pvlib-python into s…
cwhanse Mar 10, 2020
3a04ccd
docstring corrections, replace hack with pandas offset
cwhanse Mar 13, 2020
10cd8f2
refactor bifacial merge, improve merge tests (#747)
alexandermorgan Jul 20, 2019
733e489
Implement IEC 61853 IAM calculations (#752)
adriesse Jul 26, 2019
2f6376e
remove python 2 lines (#757)
alexandermorgan Jul 26, 2019
16bdde9
fix rounding issue in linke turbidity lookup function (#755)
alexandermorgan Aug 1, 2019
c4091cc
edit DIRINDEX docstring (#760)
cwhanse Aug 6, 2019
1ac666d
added snow_model code. Needs formatting
JPalakapillyKWH Aug 8, 2019
0b6236a
starting numpy-ification
JPalakapillyKWH Aug 8, 2019
ad48714
minor changes
JPalakapillyKWH Aug 9, 2019
edb1b1e
added tests and made model faster
JPalakapillyKWH Aug 13, 2019
8f59d8b
finished tests.
JPalakapillyKWH Aug 13, 2019
0352814
fixed lint errors.
JPalakapillyKWH Aug 13, 2019
d970d20
fixed minor lint error
JPalakapillyKWH Aug 14, 2019
6fa5c95
fixed bug where model stopped prematurely
JPalakapillyKWH Aug 15, 2019
81f2c85
docstring changes mostly. Changed the location of division by 10 for …
JPalakapillyKWH Aug 16, 2019
2e13c1b
fixed tests to account for prev change
JPalakapillyKWH Aug 16, 2019
62f5df6
fixed docstring for snow slide amount
JPalakapillyKWH Aug 16, 2019
2858799
Add recombination current params to all bishop88 functions (#763)
adriesse Aug 28, 2019
539402c
Raise if no parameters provided to retrieve_sam() (#770) (#775)
Peque Aug 28, 2019
04083bf
refactor cec test data into fixtures (#774)
Peque Aug 29, 2019
3052442
remove functions marked for 0.7 removal (#772)
wholmgren Aug 29, 2019
bf79511
move celltemp functions to celltemp.py, expose celltemp.pvsyst in Mod…
cwhanse Sep 9, 2019
f5f776a
Update module and inverter files (#761) (#767)
Peque Sep 9, 2019
81f78d5
refactor repeated code in ModelChain singlediode and LocalizedPVSyste…
Peque Sep 9, 2019
f1b0c5a
Create ivtools (#718)
cwhanse Sep 11, 2019
b75909c
clean up whatsnew after #718 merge (#778)
cwhanse Sep 11, 2019
724ac68
ModelChain 0.7 deprecations, remove times kwarg (#773)
wholmgren Sep 16, 2019
cabc6c8
add timeout to get_psm3 (#741)
cwhanse Sep 16, 2019
fc179a4
Update forecast.py comment typo
aperetti Sep 23, 2019
468e42d
Get rid of `re` deprecation warnings (#787)
Peque Oct 10, 2019
ee7ef7a
Fix documentation references to inverter/module data (#791)
veronicaguo Oct 18, 2019
53d4654
Create iam.py, consistent naming for IAM functions (#783)
cwhanse Oct 18, 2019
743a4fb
add bare environment CI tests (#790)
wholmgren Oct 21, 2019
9d7683e
handle warnings from temperature model tests (#796)
cwhanse Oct 23, 2019
bf29887
replace Pandas item() implementation with numpy's using .values (#797)
CameronTStark Oct 24, 2019
6bfc18b
mark xfail of test_get_psm3 (#803)
CameronTStark Oct 29, 2019
029cf20
coefficient estimation method following DeSoto(2006) (#784)
tylunel Oct 31, 2019
dac54de
change tools._scalar_out/_array_out arg name to avoid collision with …
CameronTStark Oct 31, 2019
1b6e62d
Implement IEC 61853 IAM calculations for diffuse irradiance (#793)
adriesse Nov 2, 2019
e6c33dd
add macOS 10.14 Mojave to Azure Pipelines CI (#812)
cwhanse Nov 5, 2019
2305603
update readme docs to stable
wholmgren Nov 5, 2019
c0fda4f
Add note clearksky Ineichen term b (#814)
cedricleroy Nov 7, 2019
4250d71
Drop DataFrame as option for `module` input to pvsystem.sapm (#811)
cwhanse Nov 7, 2019
4f6584a
Don't expose model parameter dictionaries to users (#805)
veronicaguo Nov 13, 2019
f6b0e41
* Fix for issue #782 (#816)
tylunel Nov 14, 2019
bcbd29f
Add shield organization table with download shields (#820)
CameronTStark Nov 15, 2019
bef5866
fix reading MIDC files with mismatching header/data columns (#822)
alorenzo175 Nov 19, 2019
8792b60
Change units on SAPM effective irradiance from suns to W/m2 (#815)
cwhanse Nov 26, 2019
69e340f
fix handful of documentation warnings (#819)
wholmgren Nov 26, 2019
6a48f94
update contributing documentation, pr template (#818)
wholmgren Nov 26, 2019
54d535b
import bifacial module in __init__.py (#826)
wholmgren Nov 27, 2019
b5bdcce
Put SAM product renaming code in a separate function, simplify, add w…
adriesse Nov 27, 2019
ad12c56
Create scaling.py and implement WVM model (#807)
jranalli Dec 2, 2019
35daed1
Fix typo in TMY total sky cover uncertainty column name (#831)
hamiltonkibbe Dec 6, 2019
673490c
Location object creation from epw metadata (#821)
tylunel Dec 12, 2019
c52bc77
Implement IEC 61853 module temperature model (#834)
adriesse Dec 13, 2019
dd72dcd
update to numpy-1.12.0 (#830)
mikofski Dec 13, 2019
06de0aa
Docstring formatting (#833)
kevinsa5 Dec 16, 2019
2be01f0
*– Formatting of ModelChain.diode_params in pandas.DataFrame (#832)
tylunel Dec 17, 2019
3b6b7db
update whatsnew.rst, add contributors, v0.7.0 release date, address #…
CameronTStark Dec 18, 2019
7b9f922
change overlooked irradiance.total_irrad references to irradiance.get…
kandersolar Dec 23, 2019
92adcfc
refactor get_psm3 code into parse_psm3, read_psm3 (#842)
kandersolar Jan 3, 2020
cb131d6
Fix error in the irradiance unit checker. (#844)
dzimmanck Jan 9, 2020
02a604d
Add gallery of examples using sphinx-gallery (#846)
kandersolar Jan 10, 2020
ae18625
add pvgis to iotools (#845)
mikofski Jan 10, 2020
0b0f097
fix table line lengtsh, remove #noqa, use grid (#852)
mikofski Jan 10, 2020
dd504f6
Improves sapm deprecation warning checker (#854)
cwhanse Jan 12, 2020
c20d443
improve solpos tz requirements documentation (#853)
wholmgren Jan 13, 2020
618fe26
DOC: remove superscript formatting for citation callouts (#855)
kandersolar Jan 13, 2020
b5ab200
reorganize tests into subfolders and use pathlib for conftest DATA_DI…
mikofski Jan 16, 2020
e981839
add Boyle/Coello (Humboldt State Univ) soiling model (#850)
nappaillav Jan 17, 2020
e124175
update whats new for #844 and other contributors (#851)
wholmgren Jan 18, 2020
8da7354
add numfocus affiliation to sphinx docs (#862)
mikofski Jan 22, 2020
18c475f
fix tmy3 leapyear in February handling, coerce_year raises exception …
mikofski Jan 29, 2020
13500d2
Update for PySAM 2.0 (#874)
cwhanse Feb 6, 2020
08f5f19
docstring edits, move some functions to private
cwhanse Feb 7, 2020
1200266
make test_psm3.py robust to API overuse errors (#873)
CameronTStark Feb 10, 2020
578a70f
Fix backwards path sep (#876)
mikofski Feb 11, 2020
1c12459
forecast compat with pandas 1.0, fix bug in Location tz handling (#879)
wholmgren Feb 12, 2020
6c2372f
remove needs_pandas decorator (#885)
CameronTStark Feb 13, 2020
551331a
rewrite, reduce helpers, remove while loop
cwhanse Feb 13, 2020
0c9f848
Add calcparams_desoto+singlediode example to gallery (#872)
kandersolar Feb 14, 2020
8343212
fix documentation home page title (#890)
CameronTStark Feb 14, 2020
3661d86
rename functions, review responses, add subhourly test
cwhanse Feb 14, 2020
f3a2eec
fixes
cwhanse Feb 14, 2020
e80763c
temperature -> temp_air, test correction, fix default for m
cwhanse Feb 14, 2020
b92efee
put m=-80 back
cwhanse Feb 14, 2020
da22506
add Kimber soiling model (#860)
mikofski Feb 15, 2020
238acc6
move contents of 0.6.4 whatsnew into 0.7.0 whatsnew (#899)
wholmgren Feb 21, 2020
7ee7cc6
compatibility for cftime==1.1 (#900)
wholmgren Feb 21, 2020
76309cc
Use pytest remotedata (#896)
CameronTStark Feb 21, 2020
ea286cb
Include Python3.8 into Azure Pipelines (#904)
CameronTStark Feb 22, 2020
4104062
eliminate some of the test suite warnings (#906)
wholmgren Feb 24, 2020
1441d51
Add Contributing section about gallery examples (#905)
kandersolar Feb 24, 2020
23e9ef7
Expose temperature.faiman in PVSystem and ModelChain (#897)
kandersolar Feb 25, 2020
02926a5
Rename test_modelchain system fixture (#915)
veronicaguo Feb 26, 2020
f3d286c
BLD: build docs on Azure Pipelines (#909)
CameronTStark Feb 26, 2020
f70377b
fix read_tmy3 with year coerced not monotonic, breaks soiling (#910)
mikofski Feb 29, 2020
5788223
add read pvgis tmy (#907)
mikofski Mar 1, 2020
a341fd2
TST: make iotools tests robust to API downtime (#919)
CameronTStark Mar 2, 2020
891158a
Link to code of conduct (#922)
mikofski Mar 2, 2020
67f6537
edits for threshold, use first data point
cwhanse Mar 2, 2020
00bda7a
Fix most sphinx warnings (#912)
kandersolar Mar 3, 2020
7524ba8
Fancy "view on github" links in documentation (#913)
kandersolar Mar 3, 2020
52ff419
add initial snow, better logic for coverage events
cwhanse Mar 4, 2020
649a6a5
lint, text fix
cwhanse Mar 4, 2020
d183fae
TST: Use templates in Azure Pipelines config file (#926)
wholmgren Mar 4, 2020
06a3ae7
workaround for py35/pandas 0.23.4
cwhanse Mar 5, 2020
380a576
move line
cwhanse Mar 5, 2020
29e1772
improve comments
cwhanse Mar 6, 2020
1c2ad4f
improvements from review
cwhanse Mar 6, 2020
87288cf
improve docstring
cwhanse Mar 6, 2020
9eb70a9
reorder comparison, add missing inplace
cwhanse Mar 6, 2020
bf42c8f
correct test, cut/paste error
cwhanse Mar 6, 2020
82e94a4
vectorize
cwhanse Mar 9, 2020
b6919b2
correct .clip
cwhanse Mar 9, 2020
14e1390
function and file renaming
cwhanse Mar 10, 2020
21e1086
docstring corrections, replace hack with pandas offset
cwhanse Mar 13, 2020
04ded05
Merge branch 'snow_coverage_model' of https://github.com/JPalakapilly…
cwhanse Mar 18, 2020
4aaa6f1
update api.rst, whatsnew
cwhanse Mar 18, 2020
fc68b91
Merge branch 'master' of https://github.com/pvlib/pvlib-python into s…
cwhanse Mar 18, 2020
af1a2ac
fix headings
cwhanse Mar 18, 2020
aa6bbba
change to use pandas to_offset method
cwhanse Mar 19, 2020
4989c02
Update docs/sphinx/source/api.rst
cwhanse Mar 19, 2020
a41561b
Update docs/sphinx/source/whatsnew/v0.7.2.rst
cwhanse Mar 19, 2020
31ed477
Update pvlib/snow.py
cwhanse Mar 19, 2020
e37eda0
review comments
cwhanse Mar 19, 2020
86599f4
Merge branch 'snow_coverage_model' of https://github.com/JPalakapilly…
cwhanse Mar 19, 2020
21c6813
add snow to __init__.py
cwhanse Mar 19, 2020
bf66c37
add test for irregular times
cwhanse Mar 19, 2020
ae476ed
correct user name
cwhanse Mar 19, 2020
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
28 changes: 18 additions & 10 deletions docs/sphinx/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -308,25 +308,33 @@ Functions for fitting diode models
ivtools.fit_sdm_cec_sam
ivtools.fit_sdm_desoto

Losses
------
Other
-----

.. autosummary::
:toctree: generated/

losses.soiling_hsu
losses.soiling_kimber
pvsystem.retrieve_sam
pvsystem.systemdef
pvsystem.scale_voltage_current_power


Other
-----
Effects on PV System Output
===========================

.. autosummary::
:toctree: generated/

pvsystem.retrieve_sam
pvsystem.systemdef
pvsystem.scale_voltage_current_power
snow.coverage_nrel
snow.fully_covered_nrel
snow.dc_loss_nrel

.. autosummary::
:toctree: generated/

losses.soiling_hsu
losses.soiling_kimber



Tracking
Expand Down Expand Up @@ -576,4 +584,4 @@ Methods for manipulating irradiance for temporal or spatial considerations
.. autosummary::
:toctree: generated/

scaling.wvm
scaling.wvm
4 changes: 4 additions & 0 deletions docs/sphinx/source/whatsnew/v0.7.2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ Enhancements
* Add Kimber soiling model :py:func:`pvlib.losses.soiling_kimber`. (:pull:`860`)
* Add :func:`~pvlib.iotools.read_pvgis_tmy` for files downloaded using the
PVGIS tool. (:issue:`880`)
* Add new module :py:mod:`pvlib.snow` to contain models related to snow coverage and effects on a PV system. (:pull:`764`)
* Add snow coverage model :py:func:`pvlib.snow.coverage_nrel` and function to identify when modules are fully covered by snow :py:func:`pvlib.snow.fully_covered_nrel`. (:issue:`577`)
* Add function :py:func:`pvlib.snow.dc_loss_nrel` for effect of snow coverage on DC output. (:pull:`764`)

Bug fixes
~~~~~~~~~
Expand Down Expand Up @@ -83,3 +86,4 @@ Contributors
* Karthikeyan Singaravelan (:ghuser:`tirkarthi`)
* Siyan (Veronica) Guo (:ghuser:`veronicaguo`)
* Eric Fitch (:ghuser:`ericf900`)
* Joseph Palakapilly (:ghuser:`JPalakapilly`)
1 change: 1 addition & 0 deletions pvlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@
from pvlib import singlediode # noqa: F401
from pvlib import bifacial # noqa: F401
from pvlib import losses # noqa: F401
from pvlib import snow # noqa: F401
187 changes: 187 additions & 0 deletions pvlib/snow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
"""
The ``snow`` module contains functions that model module snow cover and the
associated effects on PV module output
"""

import numpy as np
import pandas as pd
from pvlib.tools import sind


def _time_delta_in_hours(times):
delta = times.to_series().diff()
return delta.dt.total_seconds().div(3600)


def fully_covered_nrel(snowfall, 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
Hourly snowfall above which snow coverage is set to the row's slant
height. [cm/hr]

Returns
----------
boolean: Series
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]_.

References
----------
.. [1] Marion, B.; Schaefer, R.; Caine, H.; Sanchez, G. (2013).
"Measured and modeled photovoltaic system energy losses from snow for
Colorado and Wisconsin locations." Solar Energy 97; pp.112-121.
.. [2] Ryberg, D; Freeman, J. "Integration, Validation, and Application
of a PV Snow Coverage Model in SAM" (2017) NREL Technical Report
NREL/TP-6A20-68705
'''
timestep = _time_delta_in_hours(snowfall.index)
hourly_snow_rate = snowfall / timestep
# if we can infer a time frequency, use first snowfall value
# otherwise the first snowfall value is ignored
freq = pd.infer_freq(snowfall.index)
if freq is not None:
timedelta = pd.tseries.frequencies.to_offset(freq) / pd.Timedelta('1h')
hourly_snow_rate.iloc[0] = snowfall[0] / timedelta
else: # can't infer frequency from index
hourly_snow_rate[0] = 0 # replaces NaN
return hourly_snow_rate > threshold_snowfall


def coverage_nrel(snowfall, poa_irradiance, temp_air, surface_tilt,
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.

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
rather than in tenths of the row slant height. As described in [1]_, model
validation focused on fixed tilt systems.

Parameters
----------
snowfall : Series
Accumulated snowfall within each time period. [cm]
poa_irradiance : Series
Total in-plane irradiance [W/m^2]
temp_air : Series
Ambient air temperature [C]
surface_tilt : numeric
Tilt of module's from horizontal, e.g. surface facing up = 0,
surface facing horizon = 90. [degrees]
initial_coverage : float, default 0
Fraction of row's slant height that is covered with snow at the
beginning of the simulation. [unitless]
threshold_snowfall : float, default 1.0
Hourly snowfall above which snow coverage is set to the row's slant
height. [cm/hr]
can_slide_coefficient : float, default -80.
Coefficient to determine if snow can slide given irradiance and air
temperature. [W/(m^2 C)]
slide_amount_coefficient : float, default 0.197
Coefficient to determine fraction of snow that slides off in one hour.
[unitless]

Returns
-------
snow_coverage : Series
The fraction of the slant height of a row of modules that is covered
by snow at each time step.

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.

References
----------
.. [1] Marion, B.; Schaefer, R.; Caine, H.; Sanchez, G. (2013).
"Measured and modeled photovoltaic system energy losses from snow for
Colorado and Wisconsin locations." Solar Energy 97; pp.112-121.
.. [2] Ryberg, D; Freeman, J. (2017). "Integration, Validation, and
Application of a PV Snow Coverage Model in SAM" NREL Technical Report
NREL/TP-6A20-68705
'''

# find times with new snowfall
new_snowfall = fully_covered_nrel(snowfall, threshold_snowfall)

# set up output Series
snow_coverage = pd.Series(np.nan, index=poa_irradiance.index)

# determine amount that snow can slide in each timestep
can_slide = temp_air > poa_irradiance / can_slide_coefficient
slide_amt = slide_amount_coefficient * sind(surface_tilt) * \
_time_delta_in_hours(poa_irradiance.index)
slide_amt[~can_slide] = 0.
# don't slide during snow events
slide_amt[new_snowfall] = 0.
# don't slide in the interval preceding the snowfall data
slide_amt.iloc[0] = 0

# build time series of cumulative slide amounts
sliding_period_ID = new_snowfall.cumsum()
cumulative_sliding = slide_amt.groupby(sliding_period_ID).cumsum()

# set up time series of snow coverage without any sliding applied
snow_coverage[new_snowfall] = 1.0
if np.isnan(snow_coverage.iloc[0]):
snow_coverage.iloc[0] = initial_coverage
snow_coverage.ffill(inplace=True)
snow_coverage -= cumulative_sliding

# clean up periods where row is completely uncovered
return snow_coverage.clip(lower=0)


def dc_loss_nrel(snow_coverage, num_strings):
'''
Calculates the fraction of DC capacity lost due to snow coverage.

DC capacity loss assumes that if a string is partially covered by snow,
the string's capacity is lost; see [1]_, Eq. 11.8.

Module orientation is accounted for by specifying the number of cell
strings in parallel along the slant height.
For example, a typical 60-cell module has 3 parallel strings, each
comprising 20 cells in series, with the cells arranged in 6 columns of 10
cells each. For a row consisting of single modules, if the module is
mounted in portrait orientation, i.e., the row slant height is along a
column of 10 cells, there is 1 string in parallel along the row slant
height, so `num_strings=1`. In contrast, if the module is mounted in
landscape orientation with the row slant height comprising 6 cells, there
are 3 parallel strings along the row slant height, so `num_strings=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] Gilman, P. et al., (2018). "SAM Photovoltaic Model Technical
Reference Update", NREL Technical Report NREL/TP-6A20-67399.
Available at https://www.nrel.gov/docs/fy18osti/67399.pdf
'''
return np.ceil(snow_coverage * num_strings) / num_strings
97 changes: 97 additions & 0 deletions pvlib/tests/test_snow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import numpy as np
import pandas as pd

from pandas.util.testing import assert_series_equal

from pvlib import snow
from pvlib.tools import sind


def test_fully_covered_nrel():
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)
expected = pd.Series([False, True, False, True, False, False, True],
index=dt)
fully_covered = snow.fully_covered_nrel(snowfall_data)
assert_series_equal(expected, fully_covered)


def test_coverage_nrel_hourly():
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_coverage = snow.coverage_nrel(
snowfall_data, poa_irradiance, temp_air, surface_tilt,
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)
assert_series_equal(expected, snow_coverage)


def test_coverage_nrel_subhourly():
surface_tilt = 45
slide_amount_coefficient = 0.197
dt = pd.date_range(start="2019-1-1 11:00:00", end="2019-1-1 14:00:00",
freq='15T')
poa_irradiance = pd.Series([400, 200, 100, 1234, 134, 982, 100, 100, 100,
100, 100, 100, 0],
index=dt)
temp_air = pd.Series([10, 2, 10, 1234, 34, 982, 10, 10, 10, 10, -10, -10,
10], index=dt)
snowfall_data = pd.Series([1, .5, .6, .4, .23, -5, .1, .1, 0., 1., 0., 0.,
0.], index=dt)
snow_coverage = snow.coverage_nrel(
snowfall_data, poa_irradiance, temp_air, surface_tilt)
slide_amt = slide_amount_coefficient * sind(surface_tilt) * 0.25
covered = np.append(np.array([1., 1., 1., 1.]),
1.0 - slide_amt * np.array([1, 2, 3, 4, 5]))
covered = np.append(covered, np.array([1., 1., 1., 1. - slide_amt]))
expected = pd.Series(covered, index=dt)
assert_series_equal(expected, snow_coverage)


def test_fully_covered_nrel_irregular():
# test when frequency is not specified and can't be inferred
dt = pd.DatetimeIndex(["2019-1-1 11:00:00", "2019-1-1 14:30:00",
"2019-1-1 15:07:00", "2019-1-1 14:00:00"])
snowfall_data = pd.Series([1, .5, .6, .4], index=dt)
snow_coverage = snow.fully_covered_nrel(snowfall_data,
threshold_snowfall=0.5)
covered = np.array([False, False, True, False])
expected = pd.Series(covered, index=dt)
assert_series_equal(expected, snow_coverage)


def test_coverage_nrel_initial():
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([0, .5, .6, .4, .23, -5, .1, .1], index=dt)
snow_coverage = snow.coverage_nrel(
snowfall_data, poa_irradiance, temp_air, surface_tilt,
initial_coverage=0.5, threshold_snowfall=1.)
slide_amt = slide_amount_coefficient * sind(surface_tilt)
covered = 0.5 - slide_amt * np.array([0, 1, 2, 3, 4, 5, 6, 7])
covered = np.where(covered < 0, 0., covered)
expected = pd.Series(covered, index=dt)
assert_series_equal(expected, snow_coverage)


def test_dc_loss_nrel():
num_strings = 8
snow_coverage = pd.Series([1, 1, .5, .6, .2, .4, 0])
expected = pd.Series([1, 1, .5, .625, .25, .5, 0])
actual = snow.dc_loss_nrel(snow_coverage, num_strings)
assert_series_equal(expected, actual)