Skip to content

Safely calculate dni from dhi and ghi. #249

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

Closed
wants to merge 42 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
9f97e50
try to use weather DataFrame for irradiance if irradiance DataFrame i…
uvchik Sep 1, 2016
d99ee18
try to determine missing columns of the irradiance DataFrame
uvchik Sep 1, 2016
0c83199
remove prepare_irradiance call from run_model
uvchik Sep 8, 2016
9ffa138
replace irradiance DF with weather DF without breaking the API
uvchik Sep 8, 2016
2aae90c
add future warning
uvchik Sep 8, 2016
326dc11
add error if irradiation data is incomplete
uvchik Sep 8, 2016
f1e17ee
rename method
uvchik Sep 8, 2016
933e57b
replace non existing function with set set comparison
uvchik Sep 8, 2016
00e0e72
fix assigned attributes in the docstring
uvchik Sep 8, 2016
0abdde4
fix self.weather assignment
uvchik Sep 8, 2016
ec5ed5f
fix layout
uvchik Sep 8, 2016
ecf6c6d
Merge branch 'master' of https://github.com/pvlib/pvlib-python
uvchik Sep 13, 2016
9e54af7
Merge branch 'master' of https://github.com/pvlib/pvlib-python
uvchik Oct 12, 2016
4d8feaf
change existing tests to new API
uvchik Oct 13, 2016
c572a07
add new tests
uvchik Oct 13, 2016
9dac5cf
add columns or update columns of existing weather data
uvchik Oct 13, 2016
7fda218
add attributes to class
uvchik Oct 13, 2016
7e0b626
adapt docstrings
uvchik Oct 13, 2016
300678c
make time an optional argument if self.times is already set
uvchik Oct 13, 2016
c74f977
complete temporary version (beta) of "complete_irradiance"
uvchik Oct 13, 2016
ef5520e
remove duplicate import
uvchik Oct 14, 2016
dfe04b1
update docstring
uvchik Oct 14, 2016
b691358
fix typo
Oct 17, 2016
5f8ce92
add deprecated parameter to docstring
Oct 17, 2016
1bff19f
set default self.weather to None instead of empty DataFrame
Oct 17, 2016
721743d
fix typos, layout changes
Oct 17, 2016
6a1a61d
avoid mutation of input data (irradiance)
Oct 17, 2016
f702c73
overwrite self.weather completly with its parameter instead of column…
Oct 17, 2016
82a4366
add more IDE config files to gitignore
Oct 17, 2016
34166c8
add whatsnew entry
Oct 17, 2016
9ff9fe4
copy doctstring changes to run_model method
Oct 17, 2016
76d89c3
make parameters optional if attributes are already set
Oct 17, 2016
6907b6c
creating basic function to calculate DNI from GHI and DHI
Oct 17, 2016
a5acebd
replace equation with new function for test purposes
Oct 17, 2016
e3eca71
change types to satisfy type checks
Oct 17, 2016
a63a48f
add example to docstring (not working and excluded from doctest)
uvchik Oct 18, 2016
6097d75
return self and describe it in the docstring
uvchik Oct 18, 2016
da8076d
add bug fix to whatsnew
uvchik Oct 18, 2016
d4ff364
add empty DataFrame directly to avoid if clauses
uvchik Oct 18, 2016
bfb2350
make error message clearer
uvchik Oct 18, 2016
3b3960f
Merge branch 'master' of github.com:uvchik/pvlib-python into safe_dni
uvchik Oct 18, 2016
e819e4e
remove warning if new function is used
uvchik Oct 18, 2016
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: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,12 @@ coverage.xml
# Translations
*.mo

# Mr Developer
# IDE's (Mr Developer, pydev, pycharm...)
.mr.developer.cfg
.project
.pydevproject
.spyderproject
.idea/

# Rope
.ropeproject
Expand Down
1 change: 1 addition & 0 deletions docs/sphinx/source/whatsnew.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ What's New

These are new features and improvements of note in each release.

.. include:: whatsnew/v0.4.2.txt
.. include:: whatsnew/v0.4.1.txt
.. include:: whatsnew/v0.4.0.txt
.. include:: whatsnew/v0.3.3.txt
Expand Down
30 changes: 30 additions & 0 deletions docs/sphinx/source/whatsnew/v0.4.2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
.. _whatsnew_0420:

v0.4.2 ()
------------------------

This is a minor release from 0.4.0. ....


Bug fixes
~~~~~~~~~

* Fixed typo in __repr__ method of ModelChain and in its regarding test. (commit: b691358b)


API Changes
~~~~~~~~~~~

* The run_model method of the ModelChain will use the weather parameter of all weather data instead of splitting it to irradiation and weather. The irradiation parameter still works but will be removed soon.(:issue:`239`)


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

* Adding a complete_irradiance method to the ModelChain to make it possible to calculate missing irradiation data from the existing columns [beta] (:issue:`239`)


Code Contributors
~~~~~~~~~~~~~~~~~

* Uwe Krien
31 changes: 31 additions & 0 deletions pvlib/irradiance.py
Original file line number Diff line number Diff line change
Expand Up @@ -1945,3 +1945,34 @@ def _get_dirint_coeffs():
[0.743440, 0.592190, 0.603060, 0.316930, 0.794390]]

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


def dni(ghi, dhi, zenith):
"""
Determine DNI from GHI and DHI.

Parameters
----------
ghi : array-like
Global horizontal irradiance in W/m^2.

dhi : array-like
Diffuse horizontal irradiance in W/m^2.

zenith : array-like
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.

Returns
-------
dni : array-like
The modeled direct normal irradiance in W/m^2

Notes
-----
Something
"""
tmp_dni = (ghi - dhi) / tools.cosd(zenith)

return tmp_dni
173 changes: 140 additions & 33 deletions pvlib/modelchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
"""

from functools import partial

import logging
import warnings
import pandas as pd

from pvlib import solarposition, pvsystem, clearsky, atmosphere
from pvlib import (solarposition, pvsystem, clearsky, atmosphere, tools)
from pvlib.tracking import SingleAxisTracker
import pvlib.irradiance # avoid name conflict with full import

Expand Down Expand Up @@ -316,9 +317,13 @@ def __init__(self, system, location,
self.losses_model = losses_model
self.orientation_strategy = orientation_strategy

self.weather = None
self.times = None
self.solar_position = None

def __repr__(self):
return ('ModelChain for: ' + str(self.system) +
' orientation_startegy: ' + str(self.orientation_strategy) +
' orientation_strategy: ' + str(self.orientation_strategy) +
' clearsky_model: ' + str(self.clearsky_model) +
' transposition_model: ' + str(self.transposition_model) +
' solar_position_method: ' + str(self.solar_position_method) +
Expand Down Expand Up @@ -602,32 +607,127 @@ def effective_irradiance_model(self):
fd*self.total_irrad['poa_diffuse'])
return self

def prepare_inputs(self, times, irradiance=None, weather=None):
def complete_irradiance(self, times=None, weather=None):
"""
Determine the missing irradiation columns. Only two of the following
data columns (dni, ghi, dhi) are needed to calculate the missing data.

This function is not safe at the moment. Results can be too high or
negative. Please contribute and help to improve this function on
https://github.com/pvlib/pvlib-python

Parameters
----------
times : DatetimeIndex
Times at which to evaluate the model. Can be None if attribute
`times` is already set.
weather : pandas.DataFrame
Table with at least two columns containing one of the following data
sets: dni, dhi, ghi. Can be None if attribute `weather` is already
set.

Returns
-------
self

Assigns attributes: times, weather

Examples
--------
This example does not work until the parameters `my_system`,
`my_location`, `my_datetime` and `my_weather` are not defined properly
but shows the basic idea how this method can be used.

>>> from pvlib.modelchain import ModelChain

>>> # my_weather containing 'dhi' and 'ghi'.
>>> mc = ModelChain(my_system, my_location) # doctest: +SKIP
>>> mc.complete_irradiance(my_datetime, my_weather) # doctest: +SKIP
>>> mc.run_model() # doctest: +SKIP

>>> # my_weather containing 'dhi', 'ghi' and 'dni'.
>>> mc = ModelChain(my_system, my_location) # doctest: +SKIP
>>> mc.run_model(my_datetime, my_weather) # doctest: +SKIP
"""
if weather is not None:
self.weather = weather
if times is not None:
self.times = times
self.solar_position = self.location.get_solarposition(self.times)
icolumns = set(self.weather.columns)
wrn_txt = ("This function is not safe at the moment.\n" +
"Results can be too high or negative.\n" +
"Help to improve this function on github:\n" +
"https://github.com/pvlib/pvlib-python \n")

if {'ghi', 'dhi'} <= icolumns and 'dni' not in icolumns:
logging.debug('Estimate dni from ghi and dhi')
self.weather.loc[:, 'dni'] = pvlib.irradiance.dni(
self.weather.loc[:, 'ghi'], self.weather.loc[:, 'dhi'],
self.solar_position.loc[:, 'zenith'])
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 *
tools.cosd(self.solar_position.zenith))

return self

def prepare_inputs(self, times=None, irradiance=None, weather=None):
"""
Prepare the solar position, irradiance, and weather inputs to
the model.

Parameters
----------
times : DatetimeIndex
Times at which to evaluate the model.
Times at which to evaluate the model. Can be None if attribute
`times` is already set.
irradiance : None or DataFrame
If None, calculates clear sky data.
Columns must be 'dni', 'ghi', 'dhi'.
This parameter is deprecated. Please use `weather` instead.
weather : None or DataFrame
If None, assumes air temperature is 20 C and
wind speed is 0 m/s.
Columns must be 'wind_speed', 'temp_air'.
If None, the weather attribute is used. If the weather attribute is
also None assumes air temperature is 20 C, wind speed is 0 m/s and
irradiation calculated from clear sky data.
Column names must be 'wind_speed', 'temp_air', 'dni', 'ghi', 'dhi'.
Do not pass incomplete irradiation data.
Use method
:py:meth:`~pvlib.modelchain.ModelChain.complete_irradiance`
instead.

Returns
-------
self

Assigns attributes: times, solar_position, airmass, irradiance,
total_irrad, weather, aoi
Assigns attributes: times, solar_position, airmass, total_irrad, aoi
"""

self.times = times
if weather is not None:
self.weather = weather
if self.weather is None:
self.weather = pd.DataFrame()

# The following part could be removed together with the irradiance
# parameter at version v0.5 or v0.6.
# **** Begin ****
wrn_txt = ("The irradiance parameter will be removed soon.\n" +
"Please use the weather parameter to pass a DataFrame " +
"with irradiance (ghi, dni, dhi), wind speed and " +
"temp_air.\n")
if irradiance is not None:
warnings.warn(wrn_txt, FutureWarning)
for column in irradiance.columns:
self.weather[column] = irradiance[column]
# **** End ****

if times is not None:
self.times = times

self.solar_position = self.location.get_solarposition(self.times)

Expand All @@ -637,12 +737,17 @@ def prepare_inputs(self, times, irradiance=None, weather=None):
self.aoi = self.system.get_aoi(self.solar_position['apparent_zenith'],
self.solar_position['azimuth'])

if irradiance is None:
irradiance = self.location.get_clearsky(
if not any([x in ['ghi', 'dni', 'dhi'] for x in self.weather.columns]):
self.weather[['ghi', 'dni', 'dhi']] = self.location.get_clearsky(
self.solar_position.index, self.clearsky_model,
zenith_data=self.solar_position['apparent_zenith'],
airmass_data=self.airmass['airmass_absolute'])
self.irradiance = irradiance

if not {'ghi', 'dni', 'dhi'} <= set(self.weather.columns):
raise ValueError(
"Uncompleted irradiance data set. Please check you input " +
"data.\nData set needs to have 'dni', 'dhi' and 'ghi'.\n" +
"Detected data: {0}".format(list(self.weather.columns)))

# PVSystem.get_irradiance and SingleAxisTracker.get_irradiance
# have different method signatures, so use partial to handle
Expand Down Expand Up @@ -670,35 +775,37 @@ def prepare_inputs(self, times, irradiance=None, weather=None):
self.solar_position['azimuth'])

self.total_irrad = get_irradiance(
self.irradiance['dni'],
self.irradiance['ghi'],
self.irradiance['dhi'],
self.weather['dni'],
self.weather['ghi'],
self.weather['dhi'],
airmass=self.airmass['airmass_relative'],
model=self.transposition_model)

if weather is None:
weather = {'wind_speed': 0, 'temp_air': 20}
self.weather = weather

if self.weather.get('wind_speed') is None:
self.weather['wind_speed'] = 0
if self.weather.get('temp_air') is None:
self.weather['temp_air'] = 20
return self

def run_model(self, times, irradiance=None, weather=None):
def run_model(self, times=None, irradiance=None, weather=None):
"""
Run the model.

Parameters
----------
times : DatetimeIndex
Times at which to evaluate the model.

Times at which to evaluate the model. Can be None if attribute
`times` is already set.
irradiance : None or DataFrame
If None, calculates clear sky data.
Columns must be 'dni', 'ghi', 'dhi'.

This parameter is deprecated. Please use `weather` instead.
weather : None or DataFrame
If None, assumes air temperature is 20 C and
wind speed is 0 m/s.
Columns must be 'wind_speed', 'temp_air'.
If None, assumes air temperature is 20 C, wind speed is 0 m/s and
irradiation calculated from clear sky data.
Column names must be 'wind_speed', 'temp_air', 'dni', 'ghi', 'dhi'.
Do not pass incomplete irradiation data.
Use method
:py:meth:`~pvlib.modelchain.ModelChain.complete_irradiance`
instead.

Returns
-------
Expand Down
Loading