Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 4237a67

Browse filesBrowse files
Allow asymmetrical rotation limits in pvlib.tracking.singleaxis (v2) (#1852)
* Modify max_angle to accept tuples in addition to single values * add text * add comment to docs/sphinx/source/whatsnew/v0.10.2 * reflow docstrings for line length limit * address PR suggestions * update type hint in SingleAxisTrackerMount * simplify whatsnew * PR number * add SingleAxisTrackerMount test * lint * remove duplicate whatsnew entry --------- Co-authored-by: MichalArieli <michal@solargik.com> Co-authored-by: MichalArieli <120458722+MichalArieli@users.noreply.github.com>
1 parent 5f7109b commit 4237a67
Copy full SHA for 4237a67

File tree

Expand file treeCollapse file tree

5 files changed

+67
-12
lines changed
Filter options
Expand file treeCollapse file tree

5 files changed

+67
-12
lines changed

‎docs/sphinx/source/whatsnew/v0.10.2.rst

Copy file name to clipboardExpand all lines: docs/sphinx/source/whatsnew/v0.10.2.rst
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ Enhancements
1515
:py:func:`pvlib.iotools.get_pvgis_hourly`, :py:func:`pvlib.iotools.get_cams`,
1616
:py:func:`pvlib.iotools.get_bsrn`, and :py:func:`pvlib.iotools.read_midc_raw_data_from_nrel`.
1717
(:pull:`1800`)
18+
* Added support for asymmetric limiting angles in :py:func:`pvlib.tracking.singleaxis`
19+
and :py:class:`~pvlib.pvsystem.SingleAxisTrackerMount. (:issue:`1777`, :pull:`1809`, :pull:`1852`)
1820
* Added option to infer threshold values for
1921
:py:func:`pvlib.clearsky.detect_clearsky` (:issue:`1808`, :pull:`1784`)
2022
* Added a continuous version of the Erbs diffuse-fraction/decomposition model.
@@ -67,6 +69,7 @@ Requirements
6769
Contributors
6870
~~~~~~~~~~~~
6971
* Adam R. Jensen (:ghuser:`AdamRJensen`)
72+
* Michal Arieli (:ghuser:`MichalArieli`)
7073
* Abigail Jones (:ghuser:`ajonesr`)
7174
* Taos Transue (:ghuser:`reepoi`)
7275
* Echedey Luis (:ghuser:`echedey-ls`)

‎pvlib/pvsystem.py

Copy file name to clipboardExpand all lines: pvlib/pvsystem.py
+16-7Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import pandas as pd
1616
from dataclasses import dataclass
1717
from abc import ABC, abstractmethod
18-
from typing import Optional
18+
from typing import Optional, Union
1919

2020
from pvlib._deprecation import deprecated, warn_deprecated
2121

@@ -1412,12 +1412,21 @@ class SingleAxisTrackerMount(AbstractMount):
14121412
A value denoting the compass direction along which the axis of
14131413
rotation lies, measured east of north. [degrees]
14141414
1415-
max_angle : float, default 90
1416-
A value denoting the maximum rotation angle
1415+
max_angle : float or tuple, default 90
1416+
A value denoting the maximum rotation angle, in decimal degrees,
14171417
of the one-axis tracker from its horizontal position (horizontal
1418-
if axis_tilt = 0). A max_angle of 90 degrees allows the tracker
1419-
to rotate to a vertical position to point the panel towards a
1420-
horizon. max_angle of 180 degrees allows for full rotation. [degrees]
1418+
if axis_tilt = 0). If a float is provided, it represents the maximum
1419+
rotation angle, and the minimum rotation angle is assumed to be the
1420+
opposite of the maximum angle. If a tuple of (min_angle, max_angle) is
1421+
provided, it represents both the minimum and maximum rotation angles.
1422+
1423+
A rotation to 'max_angle' is a counter-clockwise rotation about the
1424+
y-axis of the tracker coordinate system. For example, for a tracker
1425+
with 'axis_azimuth' oriented to the south, a rotation to 'max_angle'
1426+
is towards the west, and a rotation toward 'min_angle' is in the
1427+
opposite direction, toward the east. Hence a max_angle of 180 degrees
1428+
(equivalent to max_angle = (-180, 180)) allows the tracker to achieve
1429+
its full rotation capability.
14211430
14221431
backtrack : bool, default True
14231432
Controls whether the tracker has the capability to "backtrack"
@@ -1453,7 +1462,7 @@ class SingleAxisTrackerMount(AbstractMount):
14531462
"""
14541463
axis_tilt: float = 0.0
14551464
axis_azimuth: float = 0.0
1456-
max_angle: float = 90.0
1465+
max_angle: Union[float, tuple] = 90.0
14571466
backtrack: bool = True
14581467
gcr: float = 2.0/7.0
14591468
cross_axis_tilt: float = 0.0

‎pvlib/tests/test_pvsystem.py

Copy file name to clipboardExpand all lines: pvlib/tests/test_pvsystem.py
+9Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2428,6 +2428,15 @@ def test_SingleAxisTrackerMount_get_orientation(single_axis_tracker_mount):
24282428
assert actual[key] == pytest.approx(expected_value), err_msg
24292429

24302430

2431+
def test_SingleAxisTrackerMount_get_orientation_asymmetric_max():
2432+
mount = pvsystem.SingleAxisTrackerMount(max_angle=(-30, 45))
2433+
expected = {'surface_tilt': [45, 30], 'surface_azimuth': [90, 270]}
2434+
actual = mount.get_orientation([60, 60], [90, 270])
2435+
for key, expected_value in expected.items():
2436+
err_msg = f"{key} value incorrect"
2437+
assert actual[key] == pytest.approx(expected_value), err_msg
2438+
2439+
24312440
def test_dc_ohms_from_percent():
24322441
expected = .1425
24332442
out = pvsystem.dc_ohms_from_percent(38, 8, 3, 1, 1)

‎pvlib/tests/test_tracking.py

Copy file name to clipboardExpand all lines: pvlib/tests/test_tracking.py
+16Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,22 @@ def test_max_angle():
151151
assert_frame_equal(expect, tracker_data)
152152

153153

154+
def test_min_angle():
155+
apparent_zenith = pd.Series([60])
156+
apparent_azimuth = pd.Series([270])
157+
tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth,
158+
axis_tilt=0, axis_azimuth=0,
159+
max_angle=(-45, 50), backtrack=True,
160+
gcr=2.0/7.0)
161+
162+
expect = pd.DataFrame({'aoi': 15, 'surface_azimuth': 270,
163+
'surface_tilt': 45, 'tracker_theta': -45},
164+
index=[0], dtype=np.float64)
165+
expect = expect[SINGLEAXIS_COL_ORDER]
166+
167+
assert_frame_equal(expect, tracker_data)
168+
169+
154170
def test_backtrack():
155171
apparent_zenith = pd.Series([80])
156172
apparent_azimuth = pd.Series([90])

‎pvlib/tracking.py

Copy file name to clipboardExpand all lines: pvlib/tracking.py
+23-5Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,21 @@ def singleaxis(apparent_zenith, apparent_azimuth,
4444
A value denoting the compass direction along which the axis of
4545
rotation lies. Measured in decimal degrees east of north.
4646
47-
max_angle : float, default 90
47+
max_angle : float or tuple, default 90
4848
A value denoting the maximum rotation angle, in decimal degrees,
4949
of the one-axis tracker from its horizontal position (horizontal
50-
if axis_tilt = 0). A max_angle of 90 degrees allows the tracker
51-
to rotate to a vertical position to point the panel towards a
52-
horizon. max_angle of 180 degrees allows for full rotation.
50+
if axis_tilt = 0). If a float is provided, it represents the maximum
51+
rotation angle, and the minimum rotation angle is assumed to be the
52+
opposite of the maximum angle. If a tuple of (min_angle, max_angle) is
53+
provided, it represents both the minimum and maximum rotation angles.
54+
55+
A rotation to 'max_angle' is a counter-clockwise rotation about the
56+
y-axis of the tracker coordinate system. For example, for a tracker
57+
with 'axis_azimuth' oriented to the south, a rotation to 'max_angle'
58+
is towards the west, and a rotation toward 'min_angle' is in the
59+
opposite direction, toward the east. Hence a max_angle of 180 degrees
60+
(equivalent to max_angle = (-180, 180)) allows the tracker to achieve
61+
its full rotation capability.
5362
5463
backtrack : bool, default True
5564
Controls whether the tracker has the capability to "backtrack"
@@ -190,7 +199,16 @@ def singleaxis(apparent_zenith, apparent_azimuth,
190199

191200
# NOTE: max_angle defined relative to zero-point rotation, not the
192201
# system-plane normal
193-
tracker_theta = np.clip(tracker_theta, -max_angle, max_angle)
202+
203+
# Determine minimum and maximum rotation angles based on max_angle.
204+
# If max_angle is a single value, assume min_angle is the negative.
205+
if np.isscalar(max_angle):
206+
min_angle = -max_angle
207+
else:
208+
min_angle, max_angle = max_angle
209+
210+
# Clip tracker_theta between the minimum and maximum angles.
211+
tracker_theta = np.clip(tracker_theta, min_angle, max_angle)
194212

195213
# Calculate auxiliary angles
196214
surface = calc_surface_orientation(tracker_theta, axis_tilt, axis_azimuth)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.