8000 better nan handling in singleaxis by wholmgren · Pull Request #697 · pvlib/pvlib-python · GitHub
[go: up one dir, main page]

Skip to content

better nan handling in singleaxis #697

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 2 commits into from
May 3, 2019
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
9 changes: 7 additions & 2 deletions docs/sphinx/source/whatsnew/v0.6.2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,11 @@ Bug fixes
* Fixed :py:func:`~pvlib.irradiance.erbs` behavior when zenith is
near 90 degrees. (:issue:`681`)
* :py:func:`~pvlib.irradiance.dni` now referenced in API under
Decomposing and Combining irradiance header. (:issue:`686`)
Decomposing and Combining irradiance header. (:issue:`686`)
* Fixed NaN output from :py:func:`~pvlib.tracking.singleaxis` when sun
near horizon. (:issue:`656`)
* Fixed numpy warnings in :py:func:`~pvlib.tracking.singleaxis` when
comparing NaN values to limits. (:issue:`622`)


Testing
Expand All @@ -53,4 +57,5 @@ Contributors
* Will Holmgren (:ghuser:`wholmgren`)
* Roel Loonen (:ghuser:`roelloonen`)
* Todd Hendricks (:ghuser:`tahentx`)

* Kevin Anderson (:ghuser:`kevinsa5`)
* :ghuser:`bentomlinson`
60 changes: 35 additions & 25 deletions pvlib/test/test_tracking.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import datetime

import numpy as np
from numpy import nan
import pandas as pd
Expand Down Expand Up @@ -96,10 +94,10 @@ def test_arrays_multi():
apparent_azimuth = np.array([[180, 180], [180, 180]])
# singleaxis should fail for num dim > 1
with pytest.raises(ValueError):
tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth,
axis_tilt=0, axis_azimuth=0,
max_angle=90, backtrack=True,
gcr=2.0/7.0)
tracking.singleaxis(apparent_zenith, apparent_azimuth,
axis_tilt=0, axis_azimuth=0,
max_angle=90, backtrack=True,
gcr=2.0/7.0)
# uncomment if we ever get singleaxis to support num dim > 1 arrays
# assert isinstance(tracker_data, dict)
# expect = {'tracker_theta': np.full_like(apparent_zenith, 0),
Expand All @@ -121,7 +119,7 @@ def test_azimuth_north_south():

expect = pd.DataFrame({'tracker_theta': -60, 'aoi': 0,
'surface_azimuth': 90, 'surface_tilt': 60},
index=[0], dtype=np.float64)
index=[0], dtype=np.float64)
expect = expect[SINGLEAXIS_COL_ORDER]

assert_frame_equal(expect, tracker_data)
Expand All @@ -146,7 +144,7 @@ def test_max_angle():

expect = pd.DataFrame({'aoi': 15, 'surface_azimuth': 90,
'surface_tilt': 45, 'tracker_theta': 45},
index=[0], dtype=np.float64)
index=[0], dtype=np.float64)
expect = expect[SINGLEAXIS_COL_ORDER]

assert_frame_equal(expect, tracker_data)
Expand All @@ -163,7 +161,7 @@ def test_backtrack():

expect = pd.DataFrame({'aoi': 0, 'surface_azimuth': 90,
'surface_tilt': 80, 'tracker_theta': 80},
index=[0], dtype=np.float64)
index=[0], dtype=np.float64)
expect = expect[SINGLEAXIS_COL_ORDER]

assert_frame_equal(expect, tracker_data)
Expand All @@ -175,7 +173,7 @@ def test_backtrack():

expect = pd.DataFrame({'aoi': 52.5716, 'surface_azimuth': 90,
'surface_tilt': 27.42833, 'tracker_theta': 27.4283},
index=[0], dtype=np.float64)
index=[0], dtype=np.float64)
expect = expect[SINGLEAXIS_COL_ORDER]

assert_frame_equal(expect, tracker_data)
Expand All @@ -193,7 +191,7 @@ def test_axis_tilt():
expect = pd.DataFrame({'aoi': 7.286245, 'surface_azimuth': 142.65730,
'surface_tilt': 35.98741,
'tracker_theta': -20.88121},
index=[0], dtype=np.float64)
index=[0], dtype=np.float64)
expect = expect[SINGLEAXIS_COL_ORDER]

assert_frame_equal(expect, tracker_data)
Expand All @@ -205,7 +203,7 @@ def test_axis_tilt():

expect = pd.DataFrame({'aoi': 47.6632, 'surface_azimuth': 50.96969,
'surface_tilt': 42.5152, 'tracker_theta': 31.6655},
index=[0], dtype=np.float64)
index=[0], dtype=np.float64)
expect = expect[SINGLEAXIS_COL_ORDER]

assert_frame_equal(expect, tracker_data)
Expand All @@ -222,7 +220,7 @@ def test_axis_azimuth():

expect = pd.DataFrame({'aoi': 30, 'surface_azimuth': 180,
'surface_tilt': 0, 'tracker_theta': 0},
index=[0], dtype=np.float64)
index=[0], dtype=np.float64)
expect = expect[SINGLEAXIS_COL_ORDER]

assert_frame_equal(expect, tracker_data)
Expand All @@ -237,7 +235,7 @@ def test_axis_azimuth():

expect = pd.DataFrame({'aoi': 0, 'surface_azimuth': 180,
'surface_tilt': 30, 'tracker_theta': 30},
index=[0], dtype=np.float64)
index=[0], dtype=np.float64)
expect = expect[SINGLEAXIS_COL_ORDER]

assert_frame_equal(expect, tracker_data)
Expand Down Expand Up @@ -277,6 +275,20 @@ def test_horizon_tilted():
assert_frame_equal(out, expected)


def test_low_sun_angles():
# GH 656
result = tracking.singleaxis(
apparent_zenith=80, apparent_azimuth=338, axis_tilt=30,
axis_azimuth=180, max_angle=60, backtrack=True, gcr=0.35)
expected = {
'tracker_theta': np.array([-50.31051385]),
'aoi': np.array([61.35300178]),
'surface_azimuth': np.array([112.53615425]),
'surface_tilt': np.array([56.42233095])}
for k, v in result.items():
assert_allclose(expected[k], v)


def test_SingleAxisTracker_creation():
system = tracking.SingleAxisTracker(max_angle=45,
gcr=.25,
Expand All @@ -299,37 +311,35 @@ def test_SingleAxisTracker_tracking():

tracker_data = system.singleaxis(apparent_zenith, apparent_azimuth)

expect = pd.DataFrame({'aoi': 7.286245, 'surface_azimuth': 142.65730 ,
expect = pd.DataFrame({'aoi': 7.286245, 'surface_azimuth': 142.65730,
'surface_tilt': 35.98741,
'tracker_theta': -20.88121},
index=[0], dtype=np.float64)
index=[0], dtype=np.float64)
expect = expect[SINGLEAXIS_COL_ORDER]

assert_frame_equal(expect, tracker_data)

### results calculated using PVsyst
# results calculated using PVsyst
pvsyst_solar_azimuth = 7.1609
pvsyst_solar_height = 27.315
pvsyst_axis_tilt = 20.
pvsyst_axis_azimuth = 20.
pvsyst_system = tracking.SingleAxisTracker(max_angle=60.,
axis_tilt=pvsyst_axis_tilt,
axis_azimuth=180+pvsyst_axis_azimuth,
backtrack=False)
pvsyst_system = tracking.SingleAxisTracker(
max_angle=60., axis_tilt=pvsyst_axis_tilt,
axis_azimuth=180+pvsyst_axis_azimuth, backtrack=False)
# the definition of azimuth is different from PYsyst
apparent_azimuth = pd.Series([180+pvsyst_solar_azimuth])
apparent_zenith = pd.Series([90-pvsyst_solar_height])
tracker_data = pvsyst_system.singleaxis(apparent_zenith, apparent_azimuth)
expect = pd.DataFrame({'aoi': 41.07852 , 'surface_azimuth': 180-18.432,
'surface_tilt': 24.92122 ,
expect = pd.DataFrame({'aoi': 41.07852, 'surface_azimuth': 180-18.432,
'surface_tilt': 24.92122,
'tracker_theta': -15.18391},
index=[0], dtype=np.float64)
index=[0], dtype=np.float64)
expect = expect[SINGLEAXIS_COL_ORDER]

assert_frame_equal(expect, tracker_data)



def test_LocalizedSingleAxisTracker_creation():
localized_system = tracking.LocalizedSingleAxisTracker(latitude=32,
longitude=-111,
Expand Down
16 changes: 10 additions & 6 deletions pvlib/tracking.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,19 +441,22 @@ def singleaxis(apparent_zenith, apparent_azimuth,
# angle convention being used here.
if backtrack:
axes_distance = 1/gcr
temp = np.minimum(axes_distance*cosd(wid), 1)
# clip needed for low angles. GH 656
temp = np.clip(axes_distance*cosd(wid), -1, 1)

# backtrack angle
# (always positive b/c acosd returns values between 0 and 180)
wc = np.degrees(np.arccos(temp))

# Eq 4 applied when wid in QIV (wid < 0 evalulates True), QI
tracker_theta = np.where(wid < 0, wid + wc, wid - wc)
with np.errstate(invalid='ignore'):
# errstate for GH 622
tracker_theta = np.where(wid < 0, wid + wc, wid - wc)
else:
tracker_theta = wid

tracker_theta[tracker_theta > max_angle] = max_angle
tracker_theta[tracker_theta < -max_angle] = -max_angle
tracker_theta = np.minimum(tracker_theta, max_angle)
tracker_theta = np.maximum(tracker_theta, -max_angle)

# calculate panel normal vector in panel-oriented x, y, z coordinates.
# y-axis is axis of tracker rotation. tracker_theta is a compass angle
Expand Down Expand Up @@ -560,8 +563,9 @@ def singleaxis(apparent_zenith, apparent_azimuth,
surface_azimuth = 90 - surface_azimuth + axis_azimuth

# 5. Map azimuth into [0,360) domain.
surface_azimuth[surface_azimuth < 0] += 360
surface_azimuth[surface_azimuth >= 360] -= 360
# surface_azimuth[surface_azimuth < 0] += 360
# surface_azimuth[surface_azimuth >= 360] -= 360
surface_azimuth = surface_azimuth % 360

# Calculate surface_tilt
dotproduct = (panel_norm_earth * projected_normal).sum(axis=1)
Expand Down
0