Skip to content

add SingleAxisTracker.get_aoi, refactor SingleAxisTracker.get_irradiance, fix ModelChain with SAT #356

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 5 commits into from
Aug 1, 2017
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
6 changes: 6 additions & 0 deletions docs/sphinx/source/whatsnew/v0.4.6.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,21 @@ Bug fixes
(:issue:`330`)
* Fix the `__repr__` method of `ModelChain`, crashing when
`orientation_strategy` is set to `'None'` (:issue:`352`)
* Fix the `ModelChain`'s angle of incidence calculation for
SingleAxisTracker objects (:issue:`351`)


Enhancements
~~~~~~~~~~~~
* Added default values to docstrings of all functions (:issue:`336`)


API Changes
~~~~~~~~~~~
* Removed parameter w from _calc_d (:issue:`344`)
* SingleAxisTracker.get_aoi and SingleAxisTracker.get_irradiance
now require surface_zenith and surface_azimuth (:issue:`351`)


Documentation
~~~~~~~~~~~~~
Expand Down
18 changes: 10 additions & 8 deletions pvlib/modelchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -757,9 +757,6 @@ def prepare_inputs(self, times=None, irradiance=None, weather=None):
self.airmass = self.location.get_airmass(
solar_position=self.solar_position, model=self.airmass_model)

self.aoi = self.system.get_aoi(self.solar_position['apparent_zenith'],
self.solar_position['azimuth'])

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,
Expand All @@ -773,7 +770,8 @@ def prepare_inputs(self, times=None, irradiance=None, weather=None):
"Detected data: {0}".format(list(self.weather.columns)))

# PVSystem.get_irradiance and SingleAxisTracker.get_irradiance
# have different method signatures, so use partial to handle
# and PVSystem.get_aoi and SingleAxisTracker.get_aoi
# have different method signatures. Use partial to handle
# the differences.
if isinstance(self.system, SingleAxisTracker):
self.tracking = self.system.singleaxis(
Expand All @@ -785,13 +783,17 @@ def prepare_inputs(self, times=None, irradiance=None, weather=None):
self.tracking['surface_azimuth'] = (
self.tracking['surface_azimuth']
.fillna(self.system.axis_azimuth))
self.aoi = self.tracking['aoi']
get_irradiance = partial(
self.system.get_irradiance,
surface_tilt=self.tracking['surface_tilt'],
surface_azimuth=self.tracking['surface_azimuth'],
solar_zenith=self.solar_position['apparent_zenith'],
solar_azimuth=self.solar_position['azimuth'])
self.tracking['surface_tilt'],
self.tracking['surface_azimuth'],
self.solar_position['apparent_zenith'],
self.solar_position['azimuth'])
else:
self.aoi = self.system.get_aoi(
self.solar_position['apparent_zenith'],
self.solar_position['azimuth'])
get_irradiance = partial(
self.system.get_irradiance,
self.solar_position['apparent_zenith'],
Expand Down
2 changes: 1 addition & 1 deletion pvlib/test/test_modelchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ def test_run_model_tracker(system, location):
times = pd.date_range('20160101 1200-0700', periods=2, freq='6H')
ac = mc.run_model(times).ac

expected = pd.Series(np.array([ 122.333764454, -2.00000000e-02]),
expected = pd.Series(np.array([119.067713606, nan]),
index=times)
assert_series_equal(ac, expected, check_less_precise=2)

Expand Down
40 changes: 26 additions & 14 deletions pvlib/test/test_tracking.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

import pytest
from pandas.util.testing import assert_frame_equal
from numpy.testing import assert_allclose

from pvlib.location import Location
from pvlib import solarposition
from pvlib import tracking


Expand Down Expand Up @@ -246,6 +246,21 @@ def test_SingleAxisTracker_localize_location():
assert localized_system.longitude == -111


# see test_irradiance for more thorough testing
def test_get_aoi():
system = tracking.SingleAxisTracker(max_angle=90, axis_tilt=30,
axis_azimuth=180, gcr=2.0/7.0,
backtrack=True)
surface_tilt = np.array([30, 0])
surface_azimuth = np.array([90, 270])
solar_zenith = np.array([70, 10])
solar_azimuth = np.array([100, 180])
out = system.get_aoi(surface_tilt, surface_azimuth,
solar_zenith, solar_azimuth)
expected = np.array([40.632115, 10.])
assert_allclose(out, expected, atol=0.000001)


def test_get_irradiance():
system = tracking.SingleAxisTracker(max_angle=90, axis_tilt=30,
axis_azimuth=180, gcr=2.0/7.0,
Expand All @@ -260,19 +275,17 @@ def test_get_irradiance():
solar_azimuth = solar_position['azimuth']
tracker_data = system.singleaxis(solar_zenith, solar_azimuth)

irradiance = system.get_irradiance(irrads['dni'],
irradiance = system.get_irradiance(tracker_data['surface_tilt'],
tracker_data['surface_azimuth'],
solar_zenith,
solar_azimuth,
irrads['dni'],
irrads['ghi'],
irrads['dhi'],
solar_zenith=solar_zenith,
solar_azimuth=solar_azimuth,
surface_tilt=tracker_data['surface_tilt'],
surface_azimuth=tracker_data['surface_azimuth'])
irrads['dhi'])

expected = pd.DataFrame(data=np.array(
[[ 961.80070, 815.94490, 145.85580, 135.32820,
10.52757492],
[ nan, nan, nan, nan,
nan]]),
[[961.80070, 815.94490, 145.85580, 135.32820, 10.52757492],
[nan, nan, nan, nan, nan]]),
columns=['poa_global', 'poa_direct',
'poa_diffuse', 'poa_sky_diffuse',
'poa_ground_diffuse'],
Expand All @@ -284,7 +297,7 @@ def test_get_irradiance():
def test_SingleAxisTracker___repr__():
system = tracking.SingleAxisTracker(max_angle=45, gcr=.25,
module='blah', inverter='blarg')
expected = 'SingleAxisTracker: \n axis_tilt: 0\n axis_azimuth: 0\n max_angle: 45\n backtrack: True\n gcr: 0.25\n name: None\n surface_tilt: 0\n surface_azimuth: 180\n module: blah\n inverter: blarg\n albedo: 0.25\n racking_model: open_rack_cell_glassback'
expected = 'SingleAxisTracker: \n axis_tilt: 0\n axis_azimuth: 0\n max_angle: 45\n backtrack: True\n gcr: 0.25\n name: None\n surface_tilt: None\n surface_azimuth: None\n module: blah\n inverter: blarg\n albedo: 0.25\n racking_model: open_rack_cell_glassback'
assert system.__repr__() == expected


Expand All @@ -295,7 +308,6 @@ def test_LocalizedSingleAxisTracker___repr__():
inverter='blarg',
gcr=0.25)

expected = 'LocalizedSingleAxisTracker: \n axis_tilt: 0\n axis_azimuth: 0\n max_angle: 90\n backtrack: True\n gcr: 0.25\n name: None\n surface_tilt: 0\n surface_azimuth: 180\n module: blah\n inverter: blarg\n albedo: 0.25\n racking_model: open_rack_cell_glassback\n latitude: 32\n longitude: -111\n altitude: 0\n tz: UTC'
expected = 'LocalizedSingleAxisTracker: \n axis_tilt: 0\n axis_azimuth: 0\n max_angle: 90\n backtrack: True\n gcr: 0.25\n name: None\n surface_tilt: None\n surface_azimuth: None\n module: blah\n inverter: blarg\n albedo: 0.25\n racking_model: open_rack_cell_glassback\n latitude: 32\n longitude: -111\n altitude: 0\n tz: UTC'

assert localized_system.__repr__() == expected

69 changes: 49 additions & 20 deletions pvlib/tracking.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

class SingleAxisTracker(PVSystem):
"""
Inherits all of the PV modeling methods from PVSystem.
Inherits the PV modeling methods from :ref:PVSystem:.

axis_tilt : float, default 0
The tilt of the axis of rotation (i.e, the y-axis defined by
Expand Down Expand Up @@ -54,6 +54,9 @@ def __init__(self, axis_tilt=0, axis_azimuth=0,
self.backtrack = backtrack
self.gcr = gcr

kwargs['surface_tilt'] = None
kwargs['surface_azimuth'] = None

super(SingleAxisTracker, self).__init__(**kwargs)

def __repr__(self):
Expand Down Expand Up @@ -98,20 +101,60 @@ def localize(self, location=None, latitude=None, longitude=None,

return LocalizedSingleAxisTracker(pvsystem=self, location=location)

def get_irradiance(self, dni, ghi, dhi,
def get_aoi(self, surface_tilt, surface_azimuth, solar_zenith,
solar_azimuth):
"""Get the angle of incidence on the system.

For a given set of solar zenith and azimuth angles, the
surface tilt and azimuth parameters are typically determined
by :py:method:`~SingleAxisTracker.singleaxis`. The
:py:method:`~SingleAxisTracker.singleaxis` method also returns
the angle of incidence, so this method is only needed
if using a different tracking algorithm.

Parameters
----------
surface_tilt : numeric
Panel tilt from horizontal.
surface_azimuth : numeric
Panel azimuth from north
solar_zenith : float or Series.
Solar zenith angle.
solar_azimuth : float or Series.
Solar azimuth angle.

Returns
-------
aoi : Series
The angle of incidence in degrees from normal.
"""

aoi = irradiance.aoi(surface_tilt, surface_azimuth,
solar_zenith, solar_azimuth)
return aoi

def get_irradiance(self, surface_tilt, surface_azimuth,
solar_zenith, solar_azimuth, dni, ghi, dhi,
dni_extra=None, airmass=None, model='haydavies',
**kwargs):
"""
Uses the :func:`irradiance.total_irrad` function to calculate
the plane of array irradiance components on a tilted surface
defined by ``self.surface_tilt``, ``self.surface_azimuth``, and
``self.albedo``.
defined by the input data and ``self.albedo``.

For a given set of solar zenith and azimuth angles, the
surface tilt and azimuth parameters are typically determined
by :py:method:`~SingleAxisTracker.singleaxis`.

Parameters
----------
solar_zenith : float or Series.
surface_tilt : numeric
Panel tilt from horizontal.
surface_azimuth : numeric
Panel azimuth from north
solar_zenith : numeric
Solar zenith angle.
solar_azimuth : float or Series.
solar_azimuth : numeric
Solar azimuth angle.
dni : float or Series
Direct Normal Irradiance
Expand All @@ -135,23 +178,9 @@ def get_irradiance(self, dni, ghi, dhi,
Column names are: ``total, beam, sky, ground``.
"""

surface_tilt = kwargs.pop('surface_tilt', self.surface_tilt)
surface_azimuth = kwargs.pop('surface_azimuth', self.surface_azimuth)

try:
solar_zenith = kwargs['solar_zenith']
except KeyError:
solar_zenith = self.solar_zenith

try:
solar_azimuth = kwargs['solar_azimuth']
except KeyError:
solar_azimuth = self.solar_azimuth

# not needed for all models, but this is easier
if dni_extra is None:
dni_extra = irradiance.extraradiation(solar_zenith.index)
dni_extra = pd.Series(dni_extra, index=solar_zenith.index)

if airmass is None:
airmass = atmosphere.relativeairmass(solar_zenith)
Expand Down