-
Notifications
You must be signed in to change notification settings - Fork 1.1k
add Boyle/Coello (Humboldt State Univ) soiling model #850
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
Changes from 9 commits
64d3169
501d48c
c63ca7c
0677b6c
586b6b9
114148d
23e9042
619b1a4
cd8f7d6
49d29ef
b75a690
5752479
c0a245b
ed375f3
0e5a529
b3e2e61
45c7920
e83f568
2efe2d1
23cf4ad
920d77b
e331f19
cfa5a0f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
# -*- coding: utf-8 -*- | ||
""" | ||
This module contains functions for losses of various types: soiling, mismatch, | ||
snow cover, etc. | ||
""" | ||
|
||
import numpy as np | ||
import pandas as pd | ||
from scipy.special import erf | ||
from pvlib.tools import cosd | ||
|
||
|
||
def soiling_hsu(rainfall, cleaning_threshold, tilt, pm2_5, pm10, | ||
depo_veloc={'2_5': 0.004, '10': 0.0009}, | ||
rain_accum_period=pd.Timedelta('1h')): | ||
""" | ||
Calculates soiling ratio given particulate and rain data using the model | ||
from Humboldt State University [1]_. | ||
|
||
Parameters | ||
---------- | ||
|
||
rainfall : Series | ||
Rain accumulated in each time period. [mm] | ||
|
||
cleaning_threshold : float | ||
Amount of rain in an accumulation period needed to clean the PV | ||
modules. [mm] | ||
|
||
tilt : float | ||
Tilt of the PV panels from horizontal. [degree] | ||
|
||
pm2_5 : numeric | ||
Concentration of airborne particulate matter (PM) with | ||
aerodynamic diameter less than 2.5 microns. [g/m^3] | ||
|
||
pm10 : numeric | ||
Concentration of airborne particulate matter (PM) with | ||
aerodynamicdiameter less than 10 microns. [g/m^3] | ||
|
||
depo_veloc : dict, default {'2_5': 0.4, '10': 0.09} | ||
Deposition or settling velocity of particulates. [m/s] | ||
|
||
rain_accum_period : Timedelta, default 1 hour | ||
Period for accumulating rainfall to check against `cleaning_threshold` | ||
It is recommended that `rain_accum_period` be between 1 hour and | ||
24 hours. | ||
|
||
Returns | ||
------- | ||
soiling_ratio : Series | ||
Values between 0 and 1. Equal to 1 - transmission loss. | ||
|
||
References | ||
----------- | ||
.. [1] M. Coello and L. Boyle, "Simple Model For Predicting Time Series | ||
Soiling of Photovoltaic Panels," in IEEE Journal of Photovoltaics. | ||
doi: 10.1109/JPHOTOV.2019.2919628 | ||
.. [2] Atmospheric Chemistry and Physics: From Air Pollution to Climate | ||
Change. J. Seinfeld and S. Pandis. Wiley and Sons 2001. | ||
|
||
""" | ||
|
||
# accumulate rainfall into periods for comparison with threshold | ||
accum_rain = rainfall.rolling(rain_accum_period, closed='right').sum() | ||
# cleaning is True for intervals with rainfall greater than threshold | ||
cleaning_times = accum_rain.index[accum_rain >= cleaning_threshold] | ||
|
||
horiz_mass_rate = pm2_5 * depo_veloc['2_5']\ | ||
+ np.maximum(pm10 - pm2_5, 0.) * depo_veloc['10'] | ||
tilted_mass_rate = horiz_mass_rate * cosd(tilt) # assuming no rain | ||
|
||
# tms -> tilt_mass_rate | ||
tms_cumsum = np.cumsum(tilted_mass_rate * np.ones(rainfall.shape)) | ||
|
||
mass_no_cleaning = pd.Series(index=rainfall.index, data=tms_cumsum) | ||
mass_removed = pd.Series(index=rainfall.index) | ||
mass_removed[0] = 0. | ||
mass_removed[cleaning_times] = mass_no_cleaning[cleaning_times] | ||
accum_mass = mass_no_cleaning - mass_removed.ffill() | ||
|
||
soiling_ratio = 1 - 0.3437 * erf(0.17 * accum_mass**0.8473) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh bother. It appears I've sent you off on a fruitless chase, please accept my apologies. I have Rather than add an erf function to pvlib, we should use To fix the test errors, add the following lines to at the head: on the line preceding Example here. Again, I'm very sorry that I failed to confirm numpy.erf in the numpy documentation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not a problem, It was always a great learning experience for me. |
||
|
||
return soiling_ratio |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import pandas as pd | ||
from pandas.util.testing import assert_series_equal | ||
from pvlib.losses import soiling_hsu | ||
|
||
|
||
def test_soiling_hsu(): | ||
"""Test Soiling HSU function""" | ||
dt = pd.date_range(start=pd.datetime(2019, 1, 1, 0, 0, 0), | ||
end=pd.datetime(2019, 1, 1, 23, 59, 0), freq='1h') | ||
|
||
rainfall = pd.Series( | ||
data=[0., 0., 0., 0., 1., 0., 0., 0., 0.5, 0.5, 0., 0., 0., 0., 0., | ||
0., 0.3, 0.3, 0.3, 0.3, 0., 0., 0., 0.], index=dt) | ||
|
||
pm2_5 = 1.0 | ||
pm10 = 2.0 | ||
depo_veloc = {'2_5': 1.0, '10': 1.0} | ||
tilt = 0. | ||
|
||
expected_no_cleaning = pd.Series( | ||
data=[0.884980357535360, 0.806308930084762, 0.749974647038078, | ||
0.711804155175089, 0.687489866078621, 0.672927554408964, | ||
0.664714899337491, 0.660345851212099, 0.658149551658860, | ||
0.657104593968981, 0.656633344364056, 0.656431630729954, | ||
0.656349579062171, 0.656317825078228, 0.656306121502393, | ||
0.656302009396500, 0.656300630853678, 0.656300189543417, | ||
0.656300054532516, 0.656300015031680, 0.656300003971846, | ||
0.656300001006533, 0.656300000244750, 0.656300000057132], | ||
index=dt) | ||
|
||
result1 = soiling_hsu(rainfall=rainfall, cleaning_threshold=10., tilt=tilt, | ||
pm2_5=pm2_5, pm10=pm10, depo_veloc=depo_veloc, | ||
rain_accum_period=pd.Timedelta('1h')) | ||
assert_series_equal(result1, expected_no_cleaning) | ||
|
||
# one cleaning event at 4:00 | ||
result2 = soiling_hsu(rainfall=rainfall, cleaning_threshold=1., tilt=tilt, | ||
pm2_5=pm2_5, pm10=pm10, depo_veloc=depo_veloc, | ||
rain_accum_period=pd.Timedelta('1h')) | ||
|
||
expected2 = pd.Series(index=dt) | ||
expected2[dt[:4]] = expected_no_cleaning[dt[:4]] | ||
expected2[dt[4]] = 1. | ||
expected2[dt[5:]] = expected_no_cleaning[dt[:19]] | ||
assert_series_equal(result2, expected2) | ||
|
||
# two cleaning events at 4:00-5:00 and 9:00 | ||
result3 = soiling_hsu(rainfall=rainfall, cleaning_threshold=1., tilt=tilt, | ||
pm2_5=pm2_5, pm10=pm10, depo_veloc=depo_veloc, | ||
rain_accum_period=pd.Timedelta('2h')) | ||
|
||
expected3 = pd.Series(index=dt) | ||
expected3[dt[:4]] = expected_no_cleaning[dt[:4]] | ||
expected3[dt[4:6]] = 1. | ||
expected3[dt[6:9]] = expected_no_cleaning[dt[:3]] | ||
expected3[dt[9]] = 1. | ||
expected3[dt[10:]] = expected_no_cleaning[dt[:14]] | ||
assert_series_equal(result3, expected3) | ||
|
||
# three cleaning events at 4:00-6:00, 8:00-11:00, and 17:00-20:00 | ||
result4 = soiling_hsu(rainfall=rainfall, cleaning_threshold=0.5, tilt=tilt, | ||
pm2_5=pm2_5, pm10=pm10, depo_veloc=depo_veloc, | ||
rain_accum_period=pd.Timedelta('3h')) | ||
|
||
expected4 = pd.Series(index=dt) | ||
expected4[dt[:4]] = expected_no_cleaning[dt[:4]] | ||
expected4[dt[4:7]] = 1. | ||
expected4[dt[7]] = expected_no_cleaning[dt[0]] | ||
expected4[dt[8:12]] = 1. | ||
expected4[dt[12:17]] = expected_no_cleaning[dt[:5]] | ||
expected4[dt[17:21]] = 1. | ||
expected4[dt[21:]] = expected_no_cleaning[:3] | ||
assert_series_equal(result4, expected4) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use np.erf instead of scipy.special.erf. The test fails in the py35min and the bare linux environments because we don't import scipy.