Skip to content

Safely calculate dni from dhi and ghi #250

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

Merged
merged 44 commits into from
May 29, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
7d54462
creating basic function to calculate DNI from GHI and DHI
Oct 17, 2016
b5994c6
replace equation with new function for test purposes
Oct 17, 2016
abf02af
change types to satisfy type checks
Oct 17, 2016
b9f6365
remove warning if new function is used
uvchik Oct 18, 2016
89fea56
adapt input and output of dni method
Dec 20, 2016
21f5c5f
adapt docstring to new inputs
Dec 20, 2016
a2b6413
add clearsky method to correct calculated dni
Dec 20, 2016
34865e5
add cutoff method to correct calculated dni
Dec 20, 2016
8fd63b8
add logging warning and debug messages
Dec 20, 2016
a46cdfa
increase code quality
Dec 21, 2016
98b6fe7
remove non-ascii characters
Dec 21, 2016
d3918ff
Remove user warning
birgits Jan 20, 2017
1959ed0
Replace input variable location by zenith and clearsky_dni
birgits Jan 20, 2017
f1b7030
Correct docstring
birgits Jan 20, 2017
38343cd
Remove unnecessary calculation
birgits Jan 20, 2017
35a95b4
Correct negative DNI
birgits Jan 20, 2017
e4e6139
Implement clearsky method
birgits Feb 3, 2017
3bbaddd
Include tolerance factor for clearsky method and adapt function call …
birgits Feb 6, 2017
65f7d27
Enable set_to_nan in cutoff method
birgits Feb 6, 2017
58df995
Raise value error if chosen method does not exist
birgits Feb 6, 2017
6a37e05
Debugging info how many values needed correction
birgits Feb 6, 2017
7994f28
Correct parameter types in docstring
birgits Feb 6, 2017
1ef88a4
Correct test_complete_irradiance
birgits Feb 6, 2017
4bdb7ca
Remove debugging info
birgits Apr 7, 2017
65a9261
Remove method and introduce new parameters for cutoff zenith angles
birgits Apr 7, 2017
e74faa9
Adapt docstring to new parameters
birgits Apr 7, 2017
0846db4
Remove commented lines
birgits Apr 7, 2017
6505f4f
Adapt complete_irradiance definition of modelchain to new parameters …
birgits Apr 7, 2017
46b42a3
Add test function for DNI calculation
birgits Apr 7, 2017
f3bd094
Merge upstream changes on master
birgits May 10, 2017
e1f5f58
Add accidentally un-added files
birgits May 10, 2017
9949661
Merge upstream changes on master (for real now)
birgits May 10, 2017
4398017
Remove kwargs from dni function
May 15, 2017
aa82e57
Remove unnecessary dni_tmp
May 15, 2017
0f5f113
Remove blank line at end of docstring
May 15, 2017
ac98688
Add max_dni to do calculation only once
May 15, 2017
60a922c
Correct comment
May 15, 2017
8c05db1
Correct docstring for lower_cutoff_zenith
May 15, 2017
2f08557
Remove kwargs from complete_irradiance
May 15, 2017
9df766f
Correct docstrings
May 17, 2017
e7a43df
Use same default value for clearsky_tolerance as in ModelChain
May 17, 2017
d07ab49
Change names of lower_ and upper_cutoff_zenith and adapt docstring an…
May 17, 2017
2c253cb
Merge remote-tracking branch 'upstream/master'
birgits May 29, 2017
e410d6c
add safely calculate dni from dhi and ghi (#250)
birgits May 29, 2017
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
7 changes: 7 additions & 0 deletions docs/sphinx/source/whatsnew/v0.4.5.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,16 @@ Bug fixes
* Added lower accuracy formulas for equation of time, declination, hour angle
and solar zenith.

Enhancements
~~~~~~~~~~~~~

* Added irradiance.dni method that determines DNI from GHI and DHI and corrects
unreasonable DNI values during sunrise/sunset transitions


Contributors
~~~~~~~~~~~~

* Will Holmgren
* Mark Mikofski
* Birgit Schachler
72 changes: 72 additions & 0 deletions pvlib/irradiance.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -2048,3 +2048,75 @@ def _get_dirint_coeffs():
[0.743440, 0.592190, 0.603060, 0.316930, 0.794390]]

return coeffs[1:, 1:, :, :]


def dni(ghi, dhi, zenith, clearsky_dni=None, clearsky_tolerance=1.1,
zenith_threshold_for_zero_dni=88.0,
zenith_threshold_for_clearsky_limit=80.0):
"""
Determine DNI from GHI and DHI.

When calculating the DNI from GHI and DHI the calculated DNI may be
unreasonably high or negative for zenith angles close to 90 degrees
(sunrise/sunset transitions). This function identifies unreasonable DNI
values and sets them to NaN. If the clearsky DNI is given unreasonably high
values are cut off.

Parameters
----------
ghi : Series
Global horizontal irradiance.

dhi : Series
Diffuse horizontal irradiance.

zenith : Series
True (not refraction-corrected) zenith angles in decimal
degrees. Angles must be >=0 and <=180.

clearsky_dni : None or Series
Clearsky direct normal irradiance. Default: None.

clearsky_tolerance : float
If 'clearsky_dni' is given this parameter can be used to allow a
tolerance by how much the calculated DNI value can be greater than
the clearsky value before it is identified as an unreasonable value.
Default: 1.1.

zenith_threshold_for_zero_dni : float
Non-zero DNI values for zenith angles greater than or equal to
'zenith_threshold_for_zero_dni' will be set to NaN. Default: 88.

zenith_threshold_for_clearsky_limit : float
DNI values for zenith angles greater than or equal to
'zenith_threshold_for_clearsky_limit' and smaller the
'zenith_threshold_for_zero_dni' that are greater than the clearsky DNI
(times allowed tolerance) will be corrected. Only applies if
'clearsky_dni' is not None. Default: 80.

Returns
-------
dni : Series
The modeled direct normal irradiance.
"""

# calculate DNI
dni = (ghi - dhi) / tools.cosd(zenith)

# cutoff negative values
dni[dni < 0] = float('nan')

# set non-zero DNI values for zenith angles >=
# zenith_threshold_for_zero_dni to NaN
dni[(zenith >= zenith_threshold_for_zero_dni) & (dni != 0)] = float('nan')

# correct DNI values for zenith angles greater or equal to the
# zenith_threshold_for_clearsky_limit and smaller than the
# upper_cutoff_zenith that are greater than the clearsky DNI (times
# clearsky_tolerance)
if clearsky_dni is not None:
max_dni = clearsky_dni * clearsky_tolerance
dni[(zenith >= zenith_threshold_for_clearsky_limit) &
(zenith < zenith_threshold_for_zero_dni) &
(dni > max_dni)] = max_dni
return dni
12 changes: 8 additions & 4 deletions pvlib/modelchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -676,18 +676,22 @@ def complete_irradiance(self, times=None, weather=None):
"Results can be too high or negative.\n" +
"Help to improve this function on github:\n" +
"https://github.com/pvlib/pvlib-python \n")
warnings.warn(wrn_txt, UserWarning)

if {'ghi', 'dhi'} <= icolumns and 'dni' not in icolumns:
logging.debug('Estimate dni from ghi and dhi')
self.weather.loc[:, 'dni'] = (
(self.weather.loc[:, 'ghi'] - self.weather.loc[:, 'dhi']) /
tools.cosd(self.solar_position.loc[:, 'zenith']))
self.weather.loc[:, 'dni'] = pvlib.irradiance.dni(
self.weather.loc[:, 'ghi'], self.weather.loc[:, 'dhi'],
self.solar_position.zenith,
clearsky_dni=self.location.get_clearsky(times).dni,
clearsky_tolerance=1.1)
elif {'dni', 'dhi'} <= icolumns and 'ghi' not in icolumns:
warnings.warn(wrn_txt, UserWarning)
logging.debug('Estimate ghi from dni and dhi')
self.weather.loc[:, 'ghi'] = (
self.weather.dni * tools.cosd(self.solar_position.zenith) +
self.weather.dhi)
elif {'dni', 'ghi'} <= icolumns and 'dhi' not in icolumns:
warnings.warn(wrn_txt, UserWarning)
logging.debug('Estimate dhi from dni and ghi')
self.weather.loc[:, 'dhi'] = (
self.weather.ghi - self.weather.dni *
Expand Down
19 changes: 19 additions & 0 deletions pvlib/test/test_irradiance.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@ def test_erbs_all_scalar():
for k, v in out.items():
assert_allclose(v, expected[k], 5)


@needs_numpy_1_10
def test_dirindex():
clearsky_data = tus.get_clearsky(times, model='ineichen',
Expand Down Expand Up @@ -403,3 +404,21 @@ def test_dirindex():
tol_dirint = 0.2
assert np.allclose(out.values, dirint_close_values, rtol=tol_dirint, atol=0,
equal_nan=True)


def test_dni():
ghi = pd.Series([90, 100, 100, 100, 100])
dhi = pd.Series([100, 90, 50, 50, 50])
zenith = pd.Series([80, 100, 85, 70, 85])
clearsky_dni = pd.Series([50, 50, 200, 50, 300])

dni = irradiance.dni(ghi, dhi, zenith,
clearsky_dni=clearsky_dni, clearsky_tolerance=2)
assert_series_equal(dni,
pd.Series([float('nan'), float('nan'), 400,
146.190220008, 573.685662283]))

dni = irradiance.dni(ghi, dhi, zenith)
assert_series_equal(dni,
pd.Series([float('nan'), float('nan'), 573.685662283,
146.190220008, 573.685662283]))
14 changes: 7 additions & 7 deletions pvlib/test/test_modelchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,22 +479,22 @@ def test_complete_irradiance_clean_run(system, location):
def test_complete_irradiance(system, location):
"""Check calculations"""
mc = ModelChain(system, location)
times = pd.date_range('2010-07-05 9:00:00', periods=2, freq='H')
i = pd.DataFrame({'dni': [30.354455, 77.22822],
'dhi': [372.103976116, 497.087579068],
'ghi': [356.543700, 465.44400]}, index=times)
times = pd.date_range('2010-07-05 7:00:00-0700', periods=2, freq='H')
i = pd.DataFrame({'dni': [49.756966, 62.153947],
'ghi': [372.103976116, 497.087579068],
'dhi': [356.543700, 465.44400]}, index=times)

mc.complete_irradiance(times, weather=i[['ghi', 'dni']])
assert_series_equal(mc.weather['dhi'],
pd.Series([372.103976116, 497.087579068],
pd.Series([356.543700, 465.44400],
index=times, name='dhi'))

mc.complete_irradiance(times, weather=i[['dhi', 'dni']])
assert_series_equal(mc.weather['ghi'],
pd.Series([356.543700, 465.44400],
pd.Series([372.103976116, 497.087579068],
index=times, name='ghi'))

mc.complete_irradiance(times, weather=i[['dhi', 'ghi']])
assert_series_equal(mc.weather['dni'],
pd.Series([30.354455, 77.22822],
pd.Series([49.756966, 62.153947],
index=times, name='dni'))
4 changes: 2 additions & 2 deletions pvlib/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ def cosd(angle):

Parameters
----------
angle : float
angle : float or array-like
Angle in degrees

Returns
-------
result : float
result : float or array-like
Cosine of the angle
"""

Expand Down