Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 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
15 changes: 10 additions & 5 deletions pvlib/pvsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -1410,12 +1410,17 @@ class SingleAxisTrackerMount(AbstractMount):
A value denoting the compass direction along which the axis of
rotation lies, measured east of north. [degrees]

max_angle : float, default 90
A value denoting the maximum rotation angle
max_angle : float or tuple, default 90
A value denoting the maximum rotation angle, in decimal degrees,
of the one-axis tracker from its horizontal position (horizontal
if axis_tilt = 0). A max_angle of 90 degrees allows the tracker
to rotate to a vertical position to point the panel towards a
horizon. max_angle of 180 degrees allows for full rotation. [degrees]
if axis_tilt = 0). If a float is provided, it represents the maximum
rotation angle, and the minimum rotation angle is assumed to be the
opposite of the maximum angle. If a tuple of (min_angle, max_angle)
is provided, it represents both the minimum and maximum rotation angles.

A max_angle of 90 degrees allows the tracker to rotate to a vertical
position to point the panel towards a horizon. A max_angle of 180 degrees
allows for full rotation.

backtrack : bool, default True
Controls whether the tracker has the capability to "backtrack"
Expand Down
16 changes: 16 additions & 0 deletions pvlib/tests/test_tracking.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,22 @@ def test_max_angle():
assert_frame_equal(expect, tracker_data)


def test_min_angle():
apparent_zenith = pd.Series([60])
apparent_azimuth = pd.Series([270])
tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth,
axis_tilt=0, axis_azimuth=0,
max_angle=(-45, 50), backtrack=True,
gcr=2.0/7.0)

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

assert_frame_equal(expect, tracker_data)


def test_backtrack():
apparent_zenith = pd.Series([80])
apparent_azimuth = pd.Series([90])
Expand Down
25 changes: 20 additions & 5 deletions pvlib/tracking.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,17 @@ def singleaxis(apparent_zenith, apparent_azimuth,
A value denoting the compass direction along which the axis of
rotation lies. Measured in decimal degrees east of north.

max_angle : float, default 90
max_angle : float or tuple, default 90
A value denoting the maximum rotation angle, in decimal degrees,
of the one-axis tracker from its horizontal position (horizontal
if axis_tilt = 0). A max_angle of 90 degrees allows the tracker
to rotate to a vertical position to point the panel towards a
horizon. max_angle of 180 degrees allows for full rotation.
if axis_tilt = 0). If a float is provided, it represents the maximum
rotation angle, and the minimum rotation angle is assumed to be the
opposite of the maximum angle. If a tuple of (min_angle, max_angle)
is provided, it represents both the minimum and maximum rotation angles.

Copy link
Member

@cwhanse cwhanse Jul 19, 2023

Choose a reason for hiding this comment

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

I feel like we need to explain the orientation that distinguishes min_angle from max_angle. Suggest adding a new paragraph like:

A rotation to 'max_angle' is a counter-clockwise rotation about the y-axis of the tracker coordinate system. For example, for a tracker with 'axis_azimuth' oriented to the south, a rotation to 'max_angle' is towards the west, and a rotation toward 'min_angle' is in the opposite direction, toward the east.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for this input! As you suggested, I have added this paragraph.

A max_angle of 90 degrees allows the tracker to rotate to a vertical
position to point the panel towards a horizon. A max_angle of 180 degrees
allows for full rotation.

backtrack : bool, default True
Controls whether the tracker has the capability to "backtrack"
Expand Down Expand Up @@ -190,7 +195,17 @@ def singleaxis(apparent_zenith, apparent_azimuth,

# NOTE: max_angle defined relative to zero-point rotation, not the
# system-plane normal
tracker_theta = np.clip(tracker_theta, -max_angle, max_angle)


# Determine minimum and maximum rotation angles for the tracker based on max_angle.
# If max_angle is a single value, assume min_angle is the negative of max_angle.
if np.array(max_angle).size == 1:
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if np.array(max_angle).size == 1:
if np.isscalar(max_angle):

Maybe a bit easier to read while achieving the same thing?

min_angle = -max_angle
else:
min_angle, max_angle = max_angle

# Clip tracker_theta between the minimum and maximum angles.
tracker_theta = np.clip(tracker_theta, min_angle, max_angle)

# Calculate auxiliary angles
surface = calc_surface_orientation(tracker_theta, axis_tilt, axis_azimuth)
Expand Down