PV Lib Transposition Analysis

Using PV Lib from Sandia to calculate Plane of Array Irradiance for fixed tilt and tracking projects via the Perez Transposition Algorithm

In [19]:
#libraries and constants
%matplotlib inline
import matplotlib.pyplot as plt
import seaborn as sns
sns.set(rc={"figure.figsize": (12, 6)})
import numpy as np
import pandas as pd
import pvlib
import warnings
warnings.filterwarnings('ignore')
#nrel nsrdb API key
api_key='LhVBL5knO2kvXXwbrbgAtYUL5aGbwqLfuegppGzJ'

# User entered constants
location = pvlib.location.Location(32.2,-111, 'US/Arizona', 700, 'Tucson')
year = 2010
interval='60'

# atmospheric constants
albedo=.2

# fixed tilt inputs
surface_tilt=22.5
surface_azimuth=180

# tracking inputs
axis_tilt=0
axis_azimuth=180
max_angle=52,
backtrack=True
gcr=.5

# date to plot
plotDate='2010-03-11'

NSRDB Query

In [20]:
# nsrdb query

utc='false'
attributes = 'ghi,dhi,dni,wind_speed_10m_nwp,surface_air_temperature_nwp,solar_zenith_angle'
leap_year = 'false'
name  = 'Stephen+Lightfoote'
reason_for_use = 'solar+resource'
affiliation = 'AWSTruepower'
email = 'slightfoote@awstruepower.com'
mailing_list = 'false'
url = 'http://developer.nrel.gov/api/solar/nsrdb_0512_download.csv?wkt=POINT({lon}%20{lat})&names={year}&leap_day={leap}&interval={interval}&utc={utc}&full_name={name}&email={email}&affiliation={affiliation}&mailing_list={mailing_list}&reason={reason}&api_key={api}&attributes={attr}'.format(year=year, lat=location.latitude, lon=location.longitude, leap=leap_year, interval=interval, utc=utc, name=name,email=email, mailing_list=mailing_list, affiliation=affiliation, reason=reason_for_use, api=api_key, attr=attributes)
# Return just the first 2 lines to get metadata:
info = pd.read_csv(url, nrows=1)
# See metadata for specified properties, e.g., timezone and elevation
timezone, elevation = info['Local Time Zone'], info['Elevation']

# get data
df = pd.read_csv('http://developer.nrel.gov/api'
                 '/solar/nsrdb_0512_download.csv?wkt=POINT({lon}%20{lat})&'
                 'names={year}&leap_day={leap}&interval={interval}&utc={utc}&'
                 'full_name={name}&email={email}&affiliation={affiliation}&'
                 'mailing_list={mailing_list}&reason={reason}&api_key={api}&attributes={attr}'
                 .format(year=year, lat=location.latitude, lon=location.longitude, leap=leap_year, interval=interval, utc=utc, 
                         name=name, email=email, mailing_list=mailing_list, affiliation=affiliation, 
                         reason=reason_for_use, api=api_key, attr=attributes), skiprows=2)
df = df.set_index(pd.DatetimeIndex(start='2010-01-01 00:30', end='2010-12-31 23:30', freq='1h',tz=location.tz))
df['times']= df.index

NSRDB Data

In [21]:
print(df.head(24))
                           Year  Month  Day  Hour  Minute  GHI  DHI  DNI  \
2010-01-01 00:30:00-07:00  2010      1    1     0      30    0    0    0   
2010-01-01 01:30:00-07:00  2010      1    1     1      30    0    0    0   
2010-01-01 02:30:00-07:00  2010      1    1     2      30    0    0    0   
2010-01-01 03:30:00-07:00  2010      1    1     3      30    0    0    0   
2010-01-01 04:30:00-07:00  2010      1    1     4      30    0    0    0   
2010-01-01 05:30:00-07:00  2010      1    1     5      30    0    0    0   
2010-01-01 06:30:00-07:00  2010      1    1     6      30    0    0    0   
2010-01-01 07:30:00-07:00  2010      1    1     7      30    0    0    0   
2010-01-01 08:30:00-07:00  2010      1    1     8      30  169   41  677   
2010-01-01 09:30:00-07:00  2010      1    1     9      30  355   57  855   
2010-01-01 10:30:00-07:00  2010      1    1    10      30  505   66  933   
2010-01-01 11:30:00-07:00  2010      1    1    11      30  600   70  969   
2010-01-01 12:30:00-07:00  2010      1    1    12      30  630   72  978   
2010-01-01 13:30:00-07:00  2010      1    1    13      30  594   70  964   
2010-01-01 14:30:00-07:00  2010      1    1    14      30  494   65  925   
2010-01-01 15:30:00-07:00  2010      1    1    15      30  340   56  841   
2010-01-01 16:30:00-07:00  2010      1    1    16      30  154   40  650   
2010-01-01 17:30:00-07:00  2010      1    1    17      30    0    0    0   
2010-01-01 18:30:00-07:00  2010      1    1    18      30    0    0    0   
2010-01-01 19:30:00-07:00  2010      1    1    19      30    0    0    0   
2010-01-01 20:30:00-07:00  2010      1    1    20      30    0    0    0   
2010-01-01 21:30:00-07:00  2010      1    1    21      30    0    0    0   
2010-01-01 22:30:00-07:00  2010      1    1    22      30    0    0    0   
2010-01-01 23:30:00-07:00  2010      1    1    23      30    0    0    0   

                           Wind Speed  Temperature  Solar Zenith Angle  \
2010-01-01 00:30:00-07:00    4.327612     2.697687          170.775272   
2010-01-01 01:30:00-07:00    4.456120     2.020959          163.411326   
2010-01-01 02:30:00-07:00    4.551335     1.515070          151.474093   
2010-01-01 03:30:00-07:00    4.637626     1.159784          138.865074   
2010-01-01 04:30:00-07:00    4.693408     0.872430          126.199707   
2010-01-01 05:30:00-07:00    4.709381     0.630457          113.702370   
2010-01-01 06:30:00-07:00    4.690268     0.567712          101.546156   
2010-01-01 07:30:00-07:00    4.517715     1.776666           89.930261   
2010-01-01 08:30:00-07:00    4.147372     5.077417           79.151456   
2010-01-01 09:30:00-07:00    3.994601     9.555597           69.617207   
2010-01-01 10:30:00-07:00    3.935444    13.357202           61.933674   
2010-01-01 11:30:00-07:00    3.637806    15.813928           56.873033   
2010-01-01 12:30:00-07:00    3.278610    17.375360           55.171515   
2010-01-01 13:30:00-07:00    2.900478    18.065088           57.132911   
2010-01-01 14:30:00-07:00    2.538252    17.791650           62.409904   
2010-01-01 15:30:00-07:00    2.284294    16.100671           70.251515   
2010-01-01 16:30:00-07:00    2.390200    12.952783           79.893845   
2010-01-01 17:30:00-07:00    2.850071    10.433832           90.745365   
2010-01-01 18:30:00-07:00    3.311171     9.187646          102.408722   
2010-01-01 19:30:00-07:00    3.643010     8.051050          114.595800   
2010-01-01 20:30:00-07:00    3.860350     6.976617          127.110498   
2010-01-01 21:30:00-07:00    3.998694     6.004419          139.778667   
2010-01-01 22:30:00-07:00    4.110357     5.186731          152.363023   
2010-01-01 23:30:00-07:00    4.205229     4.541650          164.167501   

                                              times  
2010-01-01 00:30:00-07:00 2010-01-01 00:30:00-07:00  
2010-01-01 01:30:00-07:00 2010-01-01 01:30:00-07:00  
2010-01-01 02:30:00-07:00 2010-01-01 02:30:00-07:00  
2010-01-01 03:30:00-07:00 2010-01-01 03:30:00-07:00  
2010-01-01 04:30:00-07:00 2010-01-01 04:30:00-07:00  
2010-01-01 05:30:00-07:00 2010-01-01 05:30:00-07:00  
2010-01-01 06:30:00-07:00 2010-01-01 06:30:00-07:00  
2010-01-01 07:30:00-07:00 2010-01-01 07:30:00-07:00  
2010-01-01 08:30:00-07:00 2010-01-01 08:30:00-07:00  
2010-01-01 09:30:00-07:00 2010-01-01 09:30:00-07:00  
2010-01-01 10:30:00-07:00 2010-01-01 10:30:00-07:00  
2010-01-01 11:30:00-07:00 2010-01-01 11:30:00-07:00  
2010-01-01 12:30:00-07:00 2010-01-01 12:30:00-07:00  
2010-01-01 13:30:00-07:00 2010-01-01 13:30:00-07:00  
2010-01-01 14:30:00-07:00 2010-01-01 14:30:00-07:00  
2010-01-01 15:30:00-07:00 2010-01-01 15:30:00-07:00  
2010-01-01 16:30:00-07:00 2010-01-01 16:30:00-07:00  
2010-01-01 17:30:00-07:00 2010-01-01 17:30:00-07:00  
2010-01-01 18:30:00-07:00 2010-01-01 18:30:00-07:00  
2010-01-01 19:30:00-07:00 2010-01-01 19:30:00-07:00  
2010-01-01 20:30:00-07:00 2010-01-01 20:30:00-07:00  
2010-01-01 21:30:00-07:00 2010-01-01 21:30:00-07:00  
2010-01-01 22:30:00-07:00 2010-01-01 22:30:00-07:00  
2010-01-01 23:30:00-07:00 2010-01-01 23:30:00-07:00  
In [22]:
df.plot()
Out[22]:
<matplotlib.axes._subplots.AxesSubplot at 0x2aedcdf4320>

Solar Position

In [23]:
solpos = location.get_solarposition(df.times)
solpos[plotDate:plotDate].plot()
Out[23]:
<matplotlib.axes._subplots.AxesSubplot at 0x2aedd2fd1d0>

Extraterrestrial Radiation

In [24]:
dni_extra = pvlib.irradiance.extraradiation(df.index)
dni_extra = pd.Series(dni_extra, index=df.index)
dni_extra.plot()
Out[24]:
<matplotlib.axes._subplots.AxesSubplot at 0x2aedd042a58>

Air Mass

In [25]:
airmass = pvlib.atmosphere.relativeairmass(solpos['apparent_zenith'])
airmass[plotDate:plotDate].plot()
Out[25]:
<matplotlib.axes._subplots.AxesSubplot at 0x2aedd348978>

Angle of Indicence

In [26]:
# angle of indicence
aoi = pvlib.irradiance.aoi(surface_tilt,surface_azimuth,solpos['apparent_zenith'], solpos['azimuth'])
aoi[plotDate:plotDate].plot()
Out[26]:
<matplotlib.axes._subplots.AxesSubplot at 0x2aede5019e8>

Plane of Array

Using the Perez Transposition Model

Fixed Tilt

In [27]:
# plane of array irradiance fixed tilt
total_irrad = pvlib.irradiance.total_irrad(surface_tilt=surface_tilt,
                                           surface_azimuth=surface_azimuth,
                                           apparent_zenith=solpos['apparent_zenith'],
                                           albedo=albedo,
                                           azimuth=solpos['azimuth'],
                                           dni=df['DNI'],
                                           ghi=df['GHI'],
                                           dhi=df['DHI'],
                                           dni_extra=dni_extra,
                                           model='perez',
                                           solar_zenith=solpos['zenith'],
                                           airmass=airmass)
In [28]:
# plot some sample data
df['poa_global_fixed']=total_irrad.poa_global
df[plotDate:plotDate][['GHI','poa_global_fixed','DNI','DHI']].plot()
df.plot(x='times',y=['poa_global_fixed','GHI','DHI'])
Out[28]:
<matplotlib.axes._subplots.AxesSubplot at 0x2aede4bf5f8>

Single Axis Tracking

In [29]:
tracker_data = pvlib.tracking.singleaxis(solpos['apparent_zenith'], solpos['azimuth'],
                                         axis_tilt=0, axis_azimuth=180, max_angle=52,
                                         backtrack=True, gcr=.5)

perez_diffuse = pvlib.irradiance.perez(tracker_data['surface_tilt'], tracker_data['surface_azimuth'], 
                                                df['DHI'], df['DNI'], dni_extra,
                                                solpos['apparent_zenith'], solpos['azimuth'],airmass=airmass)

ground_irrad = pvlib.irradiance.grounddiffuse(tracker_data['surface_tilt'], df['GHI'], albedo=albedo)
poa_global_tracking = pvlib.tools.cosd(tracker_data['aoi'])*df['DNI'] + perez_diffuse + ground_irrad
poa_global_tracking[np.isnan(poa_global_tracking)]=0

Comparison

In [30]:
df['poa_global_tracking']=poa_global_tracking
df[plotDate:plotDate][['GHI','poa_global_fixed','poa_global_tracking','DNI','DHI']].plot()
df.plot(x='times',y=['poa_global_fixed','poa_global_tracking','GHI','DHI'])
Out[30]:
<matplotlib.axes._subplots.AxesSubplot at 0x2aede9d72b0>
In [31]:
print(df[['GHI','poa_global_fixed','poa_global_tracking']].sum()*.001)
GHI                    2153.732000
poa_global_fixed       2426.863500
poa_global_tracking    2793.571272
dtype: float64