Skip to content

Commit c24bab0

Browse files
kandersolarcwhanse
andauthored
Add gallery example for cross-axis slope backtracking (#1077)
* Create plot_backtracking_sloped_terrain.py * add angle convention images * whatsnew * Apply suggestions from code review Co-authored-by: Cliff Hansen <[email protected]> * new images * reflow text for 80char limit * text improvements * truetrack -> true-track * add comment per wholmgren's suggestion * rename file Co-authored-by: Cliff Hansen <[email protected]>
1 parent 04a523f commit c24bab0

5 files changed

+178
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
"""
2+
Backtracking on sloped terrain
3+
==============================
4+
5+
Modeling backtracking for single-axis tracker arrays on sloped terrain.
6+
"""
7+
8+
# %%
9+
# Tracker systems use backtracking to avoid row-to-row shading when the
10+
# sun is low in the sky. The backtracking strategy orients the modules exactly
11+
# on the boundary between shaded and unshaded so that the modules are oriented
12+
# as much towards the sun as possible while still remaining unshaded.
13+
# Unlike the true-tracking calculation (which only depends on solar position),
14+
# calculating the backtracking angle requires knowledge of the relative spacing
15+
# of adjacent tracker rows. This example shows how the backtracking angle
16+
# changes based on a vertical offset between rows caused by sloped terrain.
17+
# It uses :py:func:`pvlib.tracking.calc_axis_tilt` and
18+
# :py:func:`pvlib.tracking.calc_cross_axis_tilt` to calculate the necessary
19+
# array geometry parameters and :py:func:`pvlib.tracking.singleaxis` to
20+
# calculate the backtracking angles.
21+
#
22+
# Angle conventions
23+
# -----------------
24+
#
25+
# First let's go over the sign conventions used for angles. In contrast to
26+
# fixed-tilt arrays where the azimuth is that of the normal to the panels, the
27+
# convention for the azimuth of a single-axis tracker is along the tracker
28+
# axis. Note that the axis azimuth is a property of the array and is distinct
29+
# from the azimuth of the panel orientation, which changes based on tracker
30+
# rotation angle. Because the tracker axis points in two directions, there are
31+
# two choices for the axis azimuth angle, and by convention (at least in the
32+
# northern hemisphere), the more southward angle is chosen:
33+
#
34+
# .. image:: ../_images/tracker_azimuth_angle_convention.png
35+
# :alt: Image showing the azimuth convention for single-axis tracker arrays.
36+
# :width: 500
37+
# :align: center
38+
#
39+
# Note that, as with fixed-tilt arrays, the axis azimuth is determined as the
40+
# angle clockwise from north. The azimuth of the terrain's slope is also
41+
# determined as an angle clockwise from north, pointing in the direction
42+
# of falling slope. So for example, a hillside that slopes down to the east
43+
# has an azimuth of 90 degrees.
44+
#
45+
# Using the axis azimuth convention above, the sign convention for tracker
46+
# rotations is given by the
47+
# `right-hand rule <https://en.wikipedia.org/wiki/Right-hand_rule>`_.
48+
# Point the right hand thumb along the axis in the direction of the axis
49+
# azimuth and the fingers curl in the direction of positive rotation angle:
50+
#
51+
# .. image:: ../_images/tracker_rotation_angle_convention.png
52+
# :alt: Image showing the rotation sign convention for single-axis trackers.
53+
# :width: 500
54+
# :align: center
55+
#
56+
# So for an array with ``axis_azimuth=180`` (tracker axis aligned perfectly
57+
# north-south), pointing the right-hand thumb along the axis azimuth has the
58+
# fingers curling towards the west, meaning rotations towards the west are
59+
# positive and rotations towards the east are negative.
60+
#
61+
# The ground slope itself is always positive, but the component of the slope
62+
# perpendicular to the tracker axes can be positive or negative. The convention
63+
# for the cross-axis slope angle follows the right-hand rule: align
64+
# the right-hand thumb along the tracker axis in the direction of the axis
65+
# azimuth and the fingers curl towards positive angles. So in this example,
66+
# with the axis azimuth coming out of the page, an east-facing, downward slope
67+
# is a negative rotation from horizontal:
68+
#
69+
# .. image:: ../_images/ground_slope_angle_convention.png
70+
# :alt: Image showing the ground slope sign convention.
71+
# :width: 500
72+
# :align: center
73+
#
74+
75+
# %%
76+
# Rotation curves
77+
# ---------------
78+
#
79+
# Now, let's plot the simple case where the tracker axes are at right angles
80+
# to the direction of the slope. In this case, the cross-axis tilt angle
81+
# is the same as the slope of the terrain and the tracker axis itself is
82+
# horizontal.
83+
84+
from pvlib import solarposition, tracking
85+
import pandas as pd
86+
import matplotlib.pyplot as plt
87+
88+
# PV system parameters
89+
tz = 'US/Eastern'
90+
lat, lon = 40, -80
91+
gcr = 0.4
92+
93+
# calculate the solar position
94+
times = pd.date_range('2019-01-01 06:00', '2019-01-01 18:00', closed='left',
95+
freq='1min', tz=tz)
96+
solpos = solarposition.get_solarposition(times, lat, lon)
97+
98+
# compare the backtracking angle at various terrain slopes
99+
fig, ax = plt.subplots()
100+
for cross_axis_tilt in [0, 5, 10]:
101+
tracker_data = tracking.singleaxis(
102+
apparent_zenith=solpos['apparent_zenith'],
103+
apparent_azimuth=solpos['azimuth'],
104+
axis_tilt=0, # flat because the axis is perpendicular to the slope
105+
axis_azimuth=180, # N-S axis, azimuth facing south
106+
max_angle=90,
107+
backtrack=True,
108+
gcr=gcr,
109+
cross_axis_tilt=cross_axis_tilt)
110+
111+
# tracker rotation is undefined at night
112+
backtracking_position = tracker_data['tracker_theta'].fillna(0)
113+
label = 'cross-axis tilt: {}°'.format(cross_axis_tilt)
114+
backtracking_position.plot(label=label, ax=ax)
115+
116+
plt.legend()
117+
plt.title('Backtracking Curves')
118+
plt.show()
119+
120+
# %%
121+
# This plot shows how backtracking changes based on the slope between rows.
122+
# For example, unlike the flat-terrain backtracking curve, the sloped-terrain
123+
# curves do not approach zero at the end of the day. Because of the vertical
124+
# offset between rows introduced by the sloped terrain, the trackers can be
125+
# slightly tilted without shading each other.
126+
#
127+
# Now let's examine the general case where the terrain slope makes an
128+
# inconvenient angle to the tracker axes. For example, consider an array
129+
# with north-south axes on terrain that slopes down to the south-south-east.
130+
# Assuming the axes are installed parallel to the ground, the northern ends
131+
# of the axes will be higher than the southern ends. But because the slope
132+
# isn't purely parallel or perpendicular to the axes, the axis tilt and
133+
# cross-axis tilt angles are not immediately obvious. We can use pvlib
134+
# to calculate them for us:
135+
136+
# terrain slopes 10 degrees downward to the south-south-east. note: because
137+
# slope_azimuth is defined in the direction of falling slope, slope_tilt is
138+
# always positive.
139+
slope_azimuth = 155
140+
slope_tilt = 10
141+
axis_azimuth = 180 # tracker axis is still N-S
142+
143+
# calculate the tracker axis tilt, assuming that the axis follows the terrain:
144+
axis_tilt = tracking.calc_axis_tilt(slope_azimuth, slope_tilt, axis_azimuth)
145+
146+
# calculate the cross-axis tilt:
147+
cross_axis_tilt = tracking.calc_cross_axis_tilt(slope_azimuth, slope_tilt,
148+
axis_azimuth, axis_tilt)
149+
150+
print('Axis tilt:', '{:0.01f}°'.format(axis_tilt))
151+
print('Cross-axis tilt:', '{:0.01f}°'.format(cross_axis_tilt))
152+
153+
# %%
154+
# And now we can pass use these values to generate the tracker curve as
155+
# before:
156+
157+
tracker_data = tracking.singleaxis(
158+
apparent_zenith=solpos['apparent_zenith'],
159+
apparent_azimuth=solpos['azimuth'],
160+
axis_tilt=axis_tilt, # no longer flat because the terrain imparts a tilt
161+
axis_azimuth=axis_azimuth,
162+
max_angle=90,
163+
backtrack=True,
164+
gcr=gcr,
165+
cross_axis_tilt=cross_axis_tilt)
166+
167+
backtracking_position = tracker_data['tracker_theta'].fillna(0)
168+
backtracking_position.plot()
169+
170+
title_template = 'Axis tilt: {:0.01f}° Cross-axis tilt: {:0.01f}°'
171+
plt.title(title_template.format(axis_tilt, cross_axis_tilt))
172+
plt.show()
173+
174+
# %%
175+
# Note that the backtracking curve is roughly mirrored compared with the
176+
# earlier example -- it is because the terrain is now sloped somewhat to the
177+
# east instead of west.
Loading
Loading
Loading

docs/sphinx/source/whatsnew/v0.8.1.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Testing
2626

2727
Documentation
2828
~~~~~~~~~~~~~
29-
29+
* Add gallery example about backtracking on sloped terrain. (:pull:`1077`)
3030

3131
Requirements
3232
~~~~~~~~~~~~

0 commit comments

Comments
 (0)