Skip to content

Implement irradiance.complete_irradiance with component sum equations #1567

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 40 commits into from
Nov 1, 2022
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
83f0990
added new irradiance function for component sum
Oct 3, 2022
5980930
added to docstrings
Oct 3, 2022
1b2e5ed
updated docstrings
Oct 3, 2022
08a18cf
updated methods with new component_sum_irradiance() function
Oct 4, 2022
21599f6
added associated unit test for the new component sum function
Oct 4, 2022
fbf0ee9
added units tests for the component_sum_irradiance() function
Oct 5, 2022
34344e9
added whatsnew file for v0.9.4
Oct 5, 2022
24771c0
Merge branch 'master' into component_sum_equations
kperrynrel Oct 5, 2022
533d63b
fixed modelchain error to stop unit test erroring
Oct 5, 2022
db45cae
updated modelchains to get rid of unit test error
Oct 5, 2022
1a554e8
removed clearsky_dni arg for cases where it's not used
Oct 5, 2022
73e9b55
updated naming conventions for component_sum_irradiance function in m…
Oct 5, 2022
789540f
updated the routines to remove pep8 errors, added to clearsky_dni doc…
Oct 5, 2022
06cb584
updated the routine to return dataframe after calculating component s…
Oct 6, 2022
b0ae6f4
updated the PR based on @kanderso-nrel review
Oct 10, 2022
e776928
made updates based on kanderso-nrel's suggestions
Oct 14, 2022
74c954d
updated all the unit tests to pass
Oct 14, 2022
bebf349
Update pvlib/irradiance.py
kperrynrel Oct 14, 2022
f35b8e0
Update pvlib/irradiance.py
kperrynrel Oct 17, 2022
98e531c
moved warning out of dni generation call (just ghi and dhi)
Oct 17, 2022
9255aa2
updated warning statement
Oct 17, 2022
ed6e106
fix sticklerci formatting errors
Oct 17, 2022
9cfde3b
Update pvlib/irradiance.py
kperrynrel Oct 25, 2022
7cc3485
updates to name-changed to 'complete_irradiance'
Oct 25, 2022
aff38d4
fixed over-indentation error
Oct 25, 2022
e595465
removed default none from function params
Oct 26, 2022
78a180d
Merge branch 'master' into component_sum_equations
kperrynrel Oct 26, 2022
4fac56f
Update docs/sphinx/source/whatsnew/v0.9.4.rst
kperrynrel Oct 28, 2022
b9ec987
made updates based on @cwhanse's recommendations
kperrynrel Oct 28, 2022
e07f516
integrate updates pull
kperrynrel Oct 28, 2022
7ce6fa6
Update pvlib/irradiance.py
kperrynrel Oct 31, 2022
881779e
Update pvlib/irradiance.py
kperrynrel Oct 31, 2022
a0d6b18
changed to pytest error raise based on @wholmgren's recommendation
kperrynrel Oct 31, 2022
99205b4
Made updates to code based on @wholmgren's recommendations
kperrynrel Oct 31, 2022
51272d4
Merge updates
kperrynrel Oct 31, 2022
01736b9
fix pep8 error 2
kperrynrel Oct 31, 2022
3958012
updated modelchains to use zenith instead of apparent zenith
kperrynrel Oct 31, 2022
f8dd098
Merge branch 'master' into component_sum_equations
kperrynrel Oct 31, 2022
725d859
check that unit tests run
kperrynrel Oct 31, 2022
65fde50
undo ModelChain.complete_irradiance test changes
kandersolar Nov 1, 2022
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
1 change: 1 addition & 0 deletions docs/sphinx/source/reference/irradiance.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Decomposing and combining irradiance
irradiance.poa_components
irradiance.get_ground_diffuse
irradiance.dni
irradiance.component_sum_irradiance

Transposition models
--------------------
Expand Down
8 changes: 6 additions & 2 deletions docs/sphinx/source/whatsnew/v0.9.4.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@ Deprecations
Enhancements
~~~~~~~~~~~~
* Multiple code style issues fixed that were reported by LGTM analysis. (:issue:`1275`, :pull:`1559`)
* Added the component sum irradiance function to calculate the component sum value for GHI, DHI, and DNI values
:py:func:`~pvlib.irradiance.component_sum_irradiance`
(:issue:`1565`, :pull:`1567`)


Bug fixes
~~~~~~~~~



Testing
~~~~~~~

Expand All @@ -34,4 +37,5 @@ Requirements

Contributors
~~~~~~~~~~~~
* Christian Orner (:ghuser:`chrisorner`)
* Kirsten Perry (:ghuser:`kperrynrel`)
* Christian Orner (:ghuser:`chrisorner`)
60 changes: 60 additions & 0 deletions pvlib/irradiance.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@

import numpy as np
import pandas as pd
import warnings

from pvlib import atmosphere, solarposition, tools
import pvlib


# see References section of get_ground_diffuse function
Expand Down Expand Up @@ -2915,3 +2917,61 @@ def dni(ghi, dhi, zenith, clearsky_dni=None, clearsky_tolerance=1.1,
(zenith < zenith_threshold_for_zero_dni) &
(dni > max_dni)] = max_dni
return dni


def component_sum_irradiance(solar_zenith,
ghi=None,
dhi=None,
dni=None,
dni_clear=None):
"""
Use the component sum equations to calculate the missing series, using
the other available time series. One of the three parameters (ghi, dhi,
dni) is passed as None, and the other associated series passed are used to
calculate the missing series value.

Parameters
----------
solar_zenith : Series
Refraction-corrected zenith angles in decimal
degrees, with datetime index. Angles must be >=0 and <=180. Must have
the same datetime index as ghi, dhi, and dni series, when available.
ghi : Series, (optional, default None)
Pandas series of dni data, with datetime index. Must have the same
datetime index as dni, dhi, and zenith series, when available.
dhi : Series, (optional, default None)
Pandas series of dni data, with datetime index. Must have the same
datetime index as ghi, dni, and zenith series, when available.
dni : Series, (optional, default None)
Pandas series of dni data, with datetime index. Must have the same
datetime index as ghi, dhi, and zenith series, when available.
dni_clear : Series, (optional, default None)
Pandas series of clearsky dni data, calculated via the
get_clearsky function. Must have the same datetime index as ghi, dhi,
dni, and zenith series, when available. This is an optional field that
is only used when calculating the DNI component sum series.

Returns
-------
component_sum_df : Dataframe
Pandas series of 'ghi', 'dhi', and 'dni' columns with datetime index
"""
if ghi is not None and dhi is not None and dni is None:
dni = pvlib.irradiance.dni(ghi, dhi, solar_zenith,
clearsky_dni=dni_clear,
clearsky_tolerance=1.1)
elif dni is not None and dhi is not None and ghi is None:
ghi = (dhi + dni * tools.cosd(solar_zenith))
elif dni is not None and ghi is not None and dhi is None:
dhi = (ghi - dni * tools.cosd(solar_zenith))
else:
wrn_txt = ("No component sum calculated. Please recheck \n"
"passed ghi, dni, and dhi parameters to check \n"
"exactly one field out of the three is set to None.")
warnings.warn(wrn_txt, UserWarning)
# Merge the outputs into a master dataframe containing 'ghi', 'dhi',
# and 'dni' columns
component_sum_df = pd.DataFrame({'ghi': ghi,
'dhi': dhi,
'dni': dni})
return component_sum_df
39 changes: 22 additions & 17 deletions pvlib/modelchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -1289,13 +1289,13 @@ def complete_irradiance(self, weather):
self._assign_times()
self.results.solar_position = self.location.get_solarposition(
self.results.times, method=self.solar_position_method)

# Calculate the irradiance using the component sum equations,
# if needed
if isinstance(weather, tuple):
for w in self.results.weather:
self._complete_irradiance(w)
else:
self._complete_irradiance(self.results.weather)

return self

def _complete_irradiance(self, weather):
Expand All @@ -1304,26 +1304,31 @@ def _complete_irradiance(self, weather):
"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:
clearsky = self.location.get_clearsky(
weather.index, solar_position=self.results.solar_position)
weather.loc[:, 'dni'] = pvlib.irradiance.dni(
weather.loc[:, 'ghi'], weather.loc[:, 'dhi'],
self.results.solar_position.zenith,
clearsky_dni=clearsky['dni'],
clearsky_tolerance=1.1)
component_sum_df = pvlib.irradiance.component_sum_irradiance(
solar_zenith=self.results.solar_position.apparent_zenith,
ghi=weather.ghi,
dhi=weather.dhi,
dni=None,
dni_clear=clearsky.dni)
weather.loc[:, 'dni'] = component_sum_df.dni
elif {'dni', 'dhi'} <= icolumns and 'ghi' not in icolumns:
warnings.warn(wrn_txt, UserWarning)
weather.loc[:, 'ghi'] = (
weather.dhi + weather.dni *
tools.cosd(self.results.solar_position.zenith)
)
component_sum_df = pvlib.irradiance.component_sum_irradiance(
solar_zenith=self.results.solar_position.apparent_zenith,
ghi=None,
dhi=weather.dhi,
dni=weather.dni)
weather.loc[:, 'ghi'] = component_sum_df.ghi
elif {'dni', 'ghi'} <= icolumns and 'dhi' not in icolumns:
warnings.warn(wrn_txt, UserWarning)
weather.loc[:, 'dhi'] = (
weather.ghi - weather.dni *
tools.cosd(self.results.solar_position.zenith))
component_sum_df = pvlib.irradiance.component_sum_irradiance(
solar_zenith=self.results.solar_position.apparent_zenith,
ghi=weather.ghi,
dhi=None,
dni=weather.dni)
weather.loc[:, 'dhi'] = component_sum_df.dhi

def _prep_inputs_solar_pos(self, weather):
"""
Expand Down
79 changes: 77 additions & 2 deletions pvlib/tests/test_irradiance.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
import pandas as pd

import pytest
from numpy.testing import assert_almost_equal, assert_allclose

from numpy.testing import assert_almost_equal, assert_allclose, assert_warns
from pvlib.location import Location
from pvlib import irradiance

from .conftest import (
Expand All @@ -31,6 +31,11 @@ def times():
tz='US/Arizona')


@pytest.fixture
def location():
return Location(32.2, -111, altitude=700)


@pytest.fixture
def irrad_data(times):
return pd.DataFrame(np.array(
Expand Down Expand Up @@ -1082,3 +1087,73 @@ def test_clearness_index_zenith_independent(airmass_kt):
airmass)
expected = pd.Series([np.nan, 0.553744437562], index=times)
assert_series_equal(out, expected)


def test_component_sum_irradiance(location):
# Generate dataframe to test on
times = pd.date_range('2010-07-05 7:00:00-0700', periods=2, freq='H')
i = pd.DataFrame({'dni': [49.63565561689957, 62.10624908037814],
'ghi': [372.103976116, 497.087579068],
'dhi': [356.543700, 465.44400]}, index=times)
# Get zenith values associated with the location
solar_position = location.get_solarposition(times,
method='nrel_numpy')
# Get the clearsky data associated with the location
clearsky = location.get_clearsky(times, solar_position=solar_position)
# Test scenario where DNI is generated via component sum equation
component_sum_df = irradiance.component_sum_irradiance(
solar_position.apparent_zenith,
ghi=i.ghi,
dhi=i.dhi,
dni=None,
dni_clear=clearsky.dni)
# Assert that the ghi, dhi, and dni series match the original dataframe
# values
assert_series_equal(component_sum_df.ghi, i.ghi)
assert_series_equal(component_sum_df.dhi, i.dhi)
assert_series_equal(component_sum_df.dni, i.dni)
# Test scenario where GHI is generated via component sum equation
component_sum_df = irradiance.component_sum_irradiance(
solar_position.apparent_zenith,
ghi=i.ghi,
dhi=i.dhi,
dni=None,
dni_clear=clearsky.dni)
# Assert that the ghi, dhi, and dni series match the original dataframe
# values
assert_series_equal(component_sum_df.ghi, i.ghi)
assert_series_equal(component_sum_df.dhi, i.dhi)
assert_series_equal(component_sum_df.dni, i.dni)
# Test scenario where DHI is generated via component sum equation
component_sum_df = irradiance.component_sum_irradiance(
solar_position.apparent_zenith,
ghi=i.ghi,
dhi=i.dhi,
dni=None,
dni_clear=clearsky.dni)
# Assert that the ghi, dhi, and dni series match the original dataframe
# values
assert_series_equal(component_sum_df.ghi, i.ghi)
assert_series_equal(component_sum_df.dhi, i.dhi)
assert_series_equal(component_sum_df.dni, i.dni)
# Test scenario where all parameters are passed (throw warning)
component_sum_df = assert_warns(UserWarning,
irradiance.component_sum_irradiance,
solar_position.apparent_zenith,
ghi=i.ghi,
dhi=i.dhi,
dni=i.dni,
dni_clear=clearsky.dni)
# Assert that the ghi, dhi, and dni series match the original dataframe
# values
assert_series_equal(component_sum_df.ghi, i.ghi)
assert_series_equal(component_sum_df.dhi, i.dhi)
assert_series_equal(component_sum_df.dni, i.dni)
# Test scenario where only one parameter is passed (throw warning)
component_sum_df = assert_warns(UserWarning,
irradiance.component_sum_irradiance,
solar_position.apparent_zenith,
ghi=None,
dhi=None,
dni=i.dni,
dni_clear=clearsky.dni)
4 changes: 2 additions & 2 deletions pvlib/tests/test_modelchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -1886,7 +1886,7 @@ def test_complete_irradiance(sapm_dc_snl_ac_system, location):
"""Check calculations"""
mc = ModelChain(sapm_dc_snl_ac_system, location)
times = pd.date_range('2010-07-05 7:00:00-0700', periods=2, freq='H')
i = pd.DataFrame({'dni': [49.756966, 62.153947],
i = pd.DataFrame({'dni': [49.63565561689957, 62.10624908037814],
'ghi': [372.103976116, 497.087579068],
'dhi': [356.543700, 465.44400]}, index=times)

Expand All @@ -1904,7 +1904,7 @@ def test_complete_irradiance(sapm_dc_snl_ac_system, location):

mc.complete_irradiance(i[['dhi', 'ghi']])
assert_series_equal(mc.results.weather['dni'],
pd.Series([49.756966, 62.153947],
pd.Series([49.63565561689957, 62.10624908037814],
Copy link
Member

Choose a reason for hiding this comment

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

Hmm, I guess this is an artifact of switching to apparent_zenith. Maybe we shouldn't actually make that change, or if we do then it needs to be mentioned in the changelog. Let's keep this as "to discuss" also.

Copy link
Member

Choose a reason for hiding this comment

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

On technical merit, apparent_zenith would be better. But the practical difference is small and not worth annoying someone trying to reproduce archived results. I could go either way: change to apparent_zenith, or name the parameter solar_zenith and give a generic description.

Copy link
Member

Choose a reason for hiding this comment

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

Any other input on this change? We have two votes neither opposed but neither in strong favor. If that's the roll call, I'd say keep it as zenith.

index=times, name='dni'))


Expand Down