Skip to content

Incorrect AOI from pvlib.tracking.singleaxis #1221

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

Closed
cwhanse opened this issue May 7, 2021 · 9 comments · Fixed by #1273
Closed

Incorrect AOI from pvlib.tracking.singleaxis #1221

cwhanse opened this issue May 7, 2021 · 9 comments · Fixed by #1273
Labels
Milestone

Comments

@cwhanse
Copy link
Member

cwhanse commented May 7, 2021

pvlib.tracking.singleaxis produces an incorrect AOI when the sun is above the earth horizon but behind the module plane.

To Reproduce
Model a fixed tilt system (90 tilt, 180 azimuth) and compare to a vertical single axis tracker with very small rotation limit.


import pandas as pd
import pytz
import pvlib
from matplotlib import pyplot as plt

loc = pvlib.location.Location(40.1134, -88.3695)

dr = pd.date_range(start='02-Jun-1998 00:00:00', end='02-Jun-1998 23:55:00',
                   freq='5T')
tz = pytz.timezone('Etc/GMT+6')
dr = dr.tz_localize(tz)
hr = dr.hour + dr.minute/60

sp = loc.get_solarposition(dr)

cs = loc.get_clearsky(dr)

tr = pvlib.tracking.singleaxis(sp['apparent_zenith'], sp['azimuth'],
                               axis_tilt=90, axis_azimuth=180, max_angle=0.01,
                               backtrack=False)

fixed = pvlib.irradiance.aoi(90, 180, sp['apparent_zenith'], sp['azimuth'])

plt.plot(hr, fixed)
plt.plot(hr, tr['aoi'])
plt.plot(hr, sp[['apparent_elevation']])
plt.show()

plt.legend(['aoi - fixed', 'aoi - tracked', 'apparent_elevation'])

Expected behavior
The AOI for the fixed tilt system shows values greater than 90 when the sun is behind the module plane. The AOI from singleaxis does not.

I think the source of the error is the use of abs in this line.

Screenshots
aoi_fixed_vs_tracked

Versions:

  • pvlib.__version__: 0.8.1

First reported by email from Jim Wilzcak (NOAA) for the PVlib Matlab function pvl_singleaxis.m

@wholmgren
Copy link
Member

I agree that abs looks like a problem.

@mikofski
Copy link
Member

This is an ancient line first committed April 3, 2015:

AOI = np.degrees(np.arccos(np.abs(np.sum(P*Norm, axis=0))))

@wholmgren
Copy link
Member

Yes, my initial port of the matlab code was as close to 1:1 as I could make it. I don't recall second guessing the abs at the time, but I certainly should have.

@cwhanse
Copy link
Member Author

cwhanse commented May 12, 2021

We (Dan and I) concluded that line is in error, in the matlab code.

@wholmgren wholmgren added this to the 0.9.0 milestone Aug 7, 2021
@wholmgren wholmgren added the bug label Aug 7, 2021
@kandersolar
Copy link
Member

Should we just replace that entire line with a call to irradiance.aoi?

@mikofski
Copy link
Member

mikofski commented Aug 8, 2021

@kanderso-nrel the shortcoming with using irradiance.aoi afaict is that it calls irradiance.aoi_projection which is redundant because the single axis tracker already calculates the solar vector and rotates it into the tracker reference frame to use the x and z components to calculate the tracker rotation.

COINCIDENTALLY, irradiance.aoi is already used in the SingleAzisTracker method get_aoi which afaict should be a static or class method because it NEVER uses self. I guess that's a separate issue. Anyway, it says this method isn't necessary, b/c singleaxis already returns AOI.

ALSO, I was thrown for a bit in irradiance.aoi_projection which doesn't have a lot of commentary, because when calculating the dot product of surface normal and solar vector, it shows z=cos(tilt)*cos(ze) first and x=sin(tilt)*sin(ze)*cos(az-surfaz) last. Whatever

Anyway, back to this, should we consider adjusting irradiance.aoi to allow the user to pass in the AOI projection as an alternate parameter? Seems a bit like a hacky workaround, but something like this:

# in irradiance.py
def aoi(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth,
       projection=None):
    if projection is None:
        projection = aoi_projection(
            surface_tilt, surface_azimuth, solar_zenith, solar_azimuth)
    # from here it's the same
    aoi_value = np.rad2deg(np.arccos(projection))
    ...

then in singleaxis we change it to this:

    # calculate angle-of-incidence on panel
    # aoi = np.degrees(np.arccos(np.abs(np.sum(sun_vec*panel_norm, axis=0))))
    projection = (xp * panel_norm[0]
                  + yp * panel_norm[1]
                  + zp * panel_norm[2])
    # can't use np.dot for 2D matrices
    # expanding arrays is about 1.5x faster than sum
    # can skip sun_vec array formation, but still need panel norm for later
    aoi = irradiance.aoi(None, None, None, None, projection=projection)

@mikofski
Copy link
Member

mikofski commented Aug 8, 2021

or maybe just to get this ball roling we use clip for now and just close it with a #TODO

@mikofski
Copy link
Member

mikofski commented Aug 8, 2021

FYI: the repro steps at the top are for a vertical tracker.

@mikofski
Copy link
Member

mikofski commented Aug 8, 2021

after #1273

Figure_1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
4 participants