Skip to content

Instantly share code, notes, and snippets.

@mikofski
Last active November 27, 2019 21:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mikofski/690fc526b0af1d42f48f2a883cee5fd4 to your computer and use it in GitHub Desktop.
Save mikofski/690fc526b0af1d42f48f2a883cee5fd4 to your computer and use it in GitHub Desktop.
pvlib_gh656
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
import pvlib
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# in Brazil so facing north
axis_azimuth = 0.0
axis_tilt = 20
max_angle = 75.0
gcr = 0.35
# Brazil, timezone is UTC-3[hrs]
starttime = '2017-01-01T00:30:00-0300'
stoptime = '2017-12-31T23:59:59-0300'
lat, lon = -27.597300, -48.549610
times = pd.DatetimeIndex(pd.date_range(
starttime, stoptime, freq='5T'))
solpos = pvlib.solarposition.get_solarposition(
times, lat, lon)
# get the early times
ts0 = '2017-01-01 05:30:00-03:00'
ts1 = '2017-01-01 12:30:00-03:00'
apparent_zenith = solpos['apparent_zenith'][ts0:ts1]
azimuth = solpos['azimuth'][ts0:ts1]
# current implementation
sat = pvlib.tracking.singleaxis(
apparent_zenith, azimuth, axis_tilt, axis_azimuth, max_angle, True, gcr)
# turn off backtracking and set max angle to 180[deg]
sat180no = pvlib.tracking.singleaxis(
apparent_zenith, azimuth, axis_tilt, axis_azimuth, max_angle=180, gcr=gcr, backtrack=False)
# calculate cos(R)
# cos(R) = L / Lx, R is rotation, L is surface length,
# Lx is shadow on ground, tracker shades when Lx > x
# x is row spacing related to GCR, x = L/GCR
lrot = np.cos(np.radians(sat180no.tracker_theta))
# proposed backtracking algorithm for sun below trackers
# Note: if tr_rot > 90[deg] then lrot < 0
# which *can* happen at low angles if axis tilt > 0
# tracker should never backtrack more than 90[deg], when lrot = 0
# if sun below trackers then use abs() to reverse direction of trackers
cos_rot = np.minimum(np.abs(lrot) / gcr, 1.0)
backtrack_rot = np.degrees(np.arccos(cos_rot))
# combine backtracking correction with the true-tracked rotation
# Note: arccosine always positive between [-90, 90] so change
# sign of backtrack correction depending on which way tracker is rotating
tracker_wbacktrack = sat180no.tracker_theta - np.sign(sat180no.tracker_theta) * backtrack_rot
# plot figure
df = pd.DataFrame({
'sat': sat.tracker_theta,
'sat180no': sat180no.tracker_theta,
'lrot': lrot,
'cos_rot': cos_rot,
'backtrack_rot': backtrack_rot,
'tracker_wbacktrack': tracker_wbacktrack})
plt.ion()
df[['sat', 'sat180no', 'tracker_wbacktrack']].iloc[:25].plot()
plt.title('proposed backtracking for sun below tracker')
plt.ylabel('tracker rotation [degrees]')
plt.yticks(np.arange(-30,200,15))
plt.grid()
from shapely.geometry.polygon import LinearRing
from shapely import affinity
from shapely.geometry import LineString
L = 1.6 # length of trackers
P = L/gcr # distance between rows
f = plt.figure('trackers') # new figure
# true track position at 5:30AM
tracker_theta = -np.radians(df.sat180no.values[0])
# tracker 1 circle
pts1 = np.radians(np.arange(360))
pts1 = np.stack((L/2*np.cos(pts1), L/2*np.sin(pts1)), axis=1)
circle1 = LinearRing(pts1)
plt.plot(*circle1.xy, ':')
# tracker 2 circle
pts2 = np.radians(np.arange(360))
pts2 = np.stack((P + L/2*np.cos(pts2), L/2*np.sin(pts2)), axis=1)
circle2 = LinearRing(pts2)
plt.plot(*circle2.xy, ':')
# tracker 1 surface
tracker1 = LineString([(-L/2, 0), (L/2, 0)])
plt.plot(*tracker1.xy, '-.')
tracker1rot = affinity.rotate(
tracker1, tracker_theta, use_radians=True)
plt.plot(*tracker1rot.xy)
# tracker 2 surface
tracker2 = LineString([(P-L/2, 0), (P+L/2, 0)])
plt.plot(*tracker2.xy, '-.')
center2 = shapely.geometry.Point((P, 0))
tracker2rot = affinity.rotate(
tracker2, angle=tracker_theta, use_radians=True, origin=center2)
plt.plot(*tracker2rot.xy)
# sunray
a, b = tracker2rot.coords
d0 = b[0] - P
d1 = b[1] - P * np.tan(tracker_theta-np.pi/2)
sunray2 = LineString([b, (d0, d1)])
plt.plot(*sunray2.xy, '--')
# backtracking
tracker_theta = -np.radians(df.tracker_wbacktrack.values[0])
# backtrack tracker 1 surface
tracker1 = LineString([(-L/2, 0), (L/2, 0)])
tracker1rot = affinity.rotate(
tracker1, tracker_theta, use_radians=True)
plt.plot(*tracker1rot.xy)
# tracker 2 surface
tracker2 = LineString([(P-L/2, 0), (P+L/2, 0)])
center2 = shapely.geometry.Point((P, 0))
tracker2rot = affinity.rotate(
tracker2, angle=tracker_theta, use_radians=True, origin=center2)
plt.plot(*tracker2rot.xy)
# parallel sunrays
sun_angle1 = np.arctan2(*reversed(np.diff(sunray1.xy)))
# sun_angle2 = np.arctan2(*reversed(np.diff(sunray2.xy)))
a, b = tracker1rot.coords
c0 = a[0] + P + L
c1 = a[1] + (P+L) * np.tan(sun_angle1)
sunray1 = LineString([a, (c0, c1)])
plt.plot(*sunray1.xy, '--')
# alternate backtracking
tracker_theta = -np.radians(df.sat.values[0])
# backtrack tracker 1 surface
tracker1 = LineString([(-L/2, 0), (L/2, 0)])
tracker1rot = affinity.rotate(
tracker1, tracker_theta, use_radians=True)
plt.plot(*tracker1rot.xy)
# tracker 2 surface
tracker2 = LineString([(P-L/2, 0), (P+L/2, 0)])
center2 = shapely.geometry.Point((P, 0))
tracker2rot = affinity.rotate(
tracker2, angle=tracker_theta, use_radians=True, origin=center2)
plt.plot(*tracker2rot.xy)
plt.gca().axis('equal')
plt.ylim([-2,6])
plt.xlim([-2,6])
plt.grid()
plt.title('Backtracking with sun below trackers')
plt.xlabel('distance between rows')
plt.ylabel('height above "system" plane')
plt.legend([
'tracker 1',
'tracker 2',
'tracker 1: system plane',
'tracker 1: true track 98.3[deg]',
'tracker 2: system plane',
'tracker 2: true track 98.3[deg]',
'sunray',
'tracker 1: backtrack 32.5[deg]',
'tracker 2: backtrack 32.5[deg]',
'parallel sunray',
'tracker 1: alt backtrack -16[deg] or 164[deg]',
'tracker 2: alt backtrack -16[deg] or 164[deg]'])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment