Skip to content

Convert irradiance.liujordan to irradiance.campbell_norman #1104

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 10 commits into from
Dec 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 2 additions & 2 deletions docs/sphinx/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ DNI estimation models
irradiance.dirint
irradiance.dirindex
irradiance.erbs
irradiance.liujordan
irradiance.campbell_norman
irradiance.gti_dirint

Clearness index models
Expand Down Expand Up @@ -520,7 +520,7 @@ Processing data
forecast.ForecastModel.cloud_cover_to_ghi_linear
forecast.ForecastModel.cloud_cover_to_irradiance_clearsky_scaling
forecast.ForecastModel.cloud_cover_to_transmittance_linear
forecast.ForecastModel.cloud_cover_to_irradiance_liujordan
forecast.ForecastModel.cloud_cover_to_irradiance_campbell_norman
forecast.ForecastModel.cloud_cover_to_irradiance
forecast.ForecastModel.kelvin_to_celsius
forecast.ForecastModel.isobaric_to_ambient_temperature
Expand Down
8 changes: 7 additions & 1 deletion docs/sphinx/source/whatsnew/v0.8.1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Breaking changes

Deprecations
~~~~~~~~~~~~

* ``pvlib.irradiance.liujordan`` is deprecated.

Enhancements
~~~~~~~~~~~~
Expand All @@ -21,6 +21,12 @@ Enhancements
multiple MPPTs (:issue:`457`, :pull:`1085`)
* Added optional ``attributes`` parameter to :py:func:`pvlib.iotools.get_psm3`
and added the option of fetching 5- and 15-minute PSM3 data. (:pull:`1086`)
* Added :py:func:`pvlib.irradiance.campbell_norman` for estimating DNI, DHI and GHI
from extraterrestrial irradiance. This function replaces ``pvlib.irradiance.liujordan``;
users of ``pvlib.irradiance.liujordan`` should note that :py:func:`pvlib.irradiance.campbell_norman`
expects different parameters.
* :py:meth:`pvlib.forecast.Forecast.cloud_cover_to_irradiance_campbell_norman`
replaces ``pvlib.forecast.Forecast.cloud_cover_to_irradiance_liujordan``.

Bug fixes
~~~~~~~~~
Expand Down
57 changes: 52 additions & 5 deletions pvlib/forecast.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@
from xml.etree.ElementTree import ParseError

from pvlib.location import Location
from pvlib.irradiance import liujordan, get_extra_radiation, disc
from pvlib.irradiance import campbell_norman, get_extra_radiation, disc
from pvlib.irradiance import _liujordan
from siphon.catalog import TDSCatalog
from siphon.ncss import NCSS

import warnings
from pvlib._deprecation import deprecated


warnings.warn(
'The forecast module algorithms and features are highly experimental. '
Expand Down Expand Up @@ -526,8 +529,48 @@ def cloud_cover_to_transmittance_linear(self, cloud_cover, offset=0.75,

return transmittance

def cloud_cover_to_irradiance_campbell_norman(self, cloud_cover, **kwargs):
"""
Estimates irradiance from cloud cover in the following steps:

1. Determine transmittance using a function of cloud cover e.g.
:py:meth:`~ForecastModel.cloud_cover_to_transmittance_linear`
2. Calculate GHI, DNI, DHI using the
:py:func:`pvlib.irradiance.campbell_norman` model

Parameters
----------
cloud_cover : Series

Returns
-------
irradiance : DataFrame
Columns include ghi, dni, dhi
"""
# in principle, get_solarposition could use the forecast
# pressure, temp, etc., but the cloud cover forecast is not
# accurate enough to justify using these minor corrections
solar_position = self.location.get_solarposition(cloud_cover.index)
dni_extra = get_extra_radiation(cloud_cover.index)

transmittance = self.cloud_cover_to_transmittance_linear(cloud_cover,
**kwargs)

irrads = campbell_norman(solar_position['apparent_zenith'],
transmittance, dni_extra=dni_extra)
irrads = irrads.fillna(0)

return irrads

@deprecated(
'0.8',
alternative='Forecast.cloud_cover_to_irradiance_campbell_norman',
name='Forecast.cloud_cover_to_irradiance_liujordan',
removal='0.9')
def cloud_cover_to_irradiance_liujordan(self, cloud_cover, **kwargs):
"""
Deprecated. Use cloud_cover_to_irradiance_campbell_norman instead.

Estimates irradiance from cloud cover in the following steps:

1. Determine transmittance using a function of cloud cover e.g.
Expand All @@ -554,9 +597,9 @@ def cloud_cover_to_irradiance_liujordan(self, cloud_cover, **kwargs):
transmittance = self.cloud_cover_to_transmittance_linear(cloud_cover,
**kwargs)

irrads = liujordan(solar_position['apparent_zenith'],
transmittance, airmass['airmass_absolute'],
dni_extra=dni_extra)
irrads = _liujordan(solar_position['apparent_zenith'],
transmittance, airmass['airmass_absolute'],
dni_extra=dni_extra)
irrads = irrads.fillna(0)

return irrads
Expand All @@ -571,7 +614,8 @@ def cloud_cover_to_irradiance(self, cloud_cover, how='clearsky_scaling',
cloud_cover : Series
how : str, default 'clearsky_scaling'
Selects the method for conversion. Can be one of
clearsky_scaling or liujordan.
clearsky_scaling or campbell_norman. Method liujordan is
deprecated.
**kwargs
Passed to the selected method.

Expand All @@ -585,6 +629,9 @@ def cloud_cover_to_irradiance(self, cloud_cover, how='clearsky_scaling',
if how == 'clearsky_scaling':
irrads = self.cloud_cover_to_irradiance_clearsky_scaling(
cloud_cover, **kwargs)
elif how == 'campbell_norman':
irrads = self.cloud_cover_to_irradiance_campbell_norman(
cloud_cover, **kwargs)
elif how == 'liujordan':
irrads = self.cloud_cover_to_irradiance_liujordan(
cloud_cover, **kwargs)
Expand Down
63 changes: 62 additions & 1 deletion pvlib/irradiance.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@

from pvlib import atmosphere, solarposition, tools

from pvlib._deprecation import deprecated


# see References section of grounddiffuse function
SURFACE_ALBEDOS = {'urban': 0.18,
'grass': 0.20,
Expand Down Expand Up @@ -2184,7 +2187,61 @@ def erbs(ghi, zenith, datetime_or_doy, min_cos_zenith=0.065, max_zenith=87):
return data


def liujordan(zenith, transmittance, airmass, dni_extra=1367.0):
def campbell_norman(zenith, transmittance, pressure=101325.0,
dni_extra=1367.0):
'''
Determine DNI, DHI, GHI from extraterrestrial flux, transmittance,
and atmospheric pressure.

Parameters
----------
zenith: pd.Series
True (not refraction-corrected) zenith angles in decimal
degrees. If Z is a vector it must be of the same size as all
other vector inputs. Z must be >=0 and <=180.

transmittance: float
Atmospheric transmittance between 0 and 1.

pressure: float, default 101325.0
Air pressure

dni_extra: float, default 1367.0
Direct irradiance incident at the top of the atmosphere.

Returns
-------
irradiance: DataFrame
Modeled direct normal irradiance, direct horizontal irradiance,
and global horizontal irradiance in W/m^2

References
----------
.. [1] Campbell, G. S., J. M. Norman (1998) An Introduction to
Environmental Biophysics. 2nd Ed. New York: Springer.
'''

tau = transmittance

airmass = atmosphere.get_relative_airmass(zenith, model='simple')
airmass = atmosphere.get_absolute_airmass(airmass, pressure=pressure)
dni = dni_extra*tau**airmass
cos_zen = tools.cosd(zenith)
dhi = 0.3 * (1.0 - tau**airmass) * dni_extra * cos_zen
ghi = dhi + dni * cos_zen

irrads = OrderedDict()
irrads['ghi'] = ghi
irrads['dni'] = dni
irrads['dhi'] = dhi

if isinstance(ghi, pd.Series):
irrads = pd.DataFrame(irrads)

return irrads


def _liujordan(zenith, transmittance, airmass, dni_extra=1367.0):
'''
Determine DNI, DHI, GHI from extraterrestrial flux, transmittance,
and optical air mass number.
Expand Down Expand Up @@ -2242,6 +2299,10 @@ def liujordan(zenith, transmittance, airmass, dni_extra=1367.0):
return irrads


liujordan = deprecated('0.8', alternative='campbellnormam',
name='liujordan', removal='0.9')(_liujordan)


def _get_perez_coefficients(perezmodel):
'''
Find coefficients for the Perez model
Expand Down
4 changes: 2 additions & 2 deletions pvlib/tests/test_forecast.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
requires_siphon,
has_siphon,
skip_windows,
requires_recent_cftime,
requires_recent_cftime
)
from conftest import RERUNS, RERUNS_DELAY

Expand Down Expand Up @@ -69,7 +69,7 @@ def model(request):
@pytest.mark.remote_data
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
def test_process_data(model):
for how in ['liujordan', 'clearsky_scaling']:
for how in ['campbell_norman', 'clearsky_scaling']:
if model.raw_data.empty:
warnings.warn('Could not test {} process_data with how={} '
'because raw_data was empty'.format(model, how))
Expand Down
21 changes: 18 additions & 3 deletions pvlib/tests/test_irradiance.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@

from pvlib import irradiance

from conftest import requires_ephem, requires_numba
from conftest import requires_ephem, requires_numba, fail_on_pvlib_version
from pvlib._deprecation import pvlibDeprecationWarning


# fixtures create realistic test input data
Expand Down Expand Up @@ -285,13 +286,27 @@ def test_get_sky_diffuse_invalid():
model='invalid')


@fail_on_pvlib_version('0.9')
def test_liujordan():
expected = pd.DataFrame(np.array(
[[863.859736967, 653.123094076, 220.65905025]]),
columns=['ghi', 'dni', 'dhi'],
index=[0])
out = irradiance.liujordan(
pd.Series([10]), pd.Series([0.5]), pd.Series([1.1]), dni_extra=1400)
with pytest.warns(pvlibDeprecationWarning):
out = irradiance.liujordan(
pd.Series([10]), pd.Series([0.5]), pd.Series([1.1]),
dni_extra=1400)
assert_frame_equal(out, expected)
Copy link
Member

Choose a reason for hiding this comment

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

This test is going to emit a deprecation warning and we want to avoid that. We could delete it or we could skip the new test_deprecated_09 test, add @fail_on_pvlib_version('0.9') around this test, and add the with pytest.warns... here

Copy link
Member Author

Choose a reason for hiding this comment

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

I prefer the option after "or"



def test_campbell_norman():
expected = pd.DataFrame(np.array(
[[863.859736967, 653.123094076, 220.65905025]]),
columns=['ghi', 'dni', 'dhi'],
index=[0])
out = irradiance.campbell_norman(
pd.Series([10]), pd.Series([0.5]), pd.Series([109764.21013135818]),
dni_extra=1400)
assert_frame_equal(out, expected)


Expand Down