1

Background

Traditionally I've used NREL SAM tool to estimate solar output. I've been experimenting with PVLIB which is great due to the open nature and flexibility, however I can't seem to reconcile the solar production estimates between PVLIB and NREL SAM.

What I've done

I'm modeling a hypothetical solar farm near Gympie QLD. I've gone to the climate.onebuiling website, and downloaded the zip folder / epw file for "AUS_QLD_Gympie.AP.945660_TMYx.2003-2017". I've then used that weather file in NREL's SAM tool using PVwatts, with the following specs;

  • 200,000 KWdc
  • Module Type = Standard
  • 1.2 DC to AC Ratio
  • 96% inverter efficiency
  • 1 axis backtracking
  • tilt = 26 degrees
  • azimuth = 0 degrees
  • GCR = 0.4
  • losses, shading & curtailment = default

In NREL SAM i get an annual Energy Yield (AC GWh) of 415.96 GWh p.a.

I then took that same epw file and converted it to a csv, keeping just the columns for ghi, dni, dhi, temp_air & wind_speed (Google Drive link to CSV file). I've used this file as an import into PVLIB. I spec'd a PVLIB system the same specs above, with addition of albedo = 0.2 and max angle = 90 degrees (Code Below).

The result I get in PVLIB is 395.61 GWh.

Problem

The results I got are pretty different. PVLIB = ~395 GWh p.a. vs SAM = ~415 GWH p.a. I expected around a 1-2% difference, but not 5%.

Figures are even worse when I compare to a PVLIB system using clearsky.ineichen (adjusted with linke_turbidity) which yields ~475 GWh p.a.

Help Requested

Anyone know why my results are so different? is there anything I can do to narrow the gap?

PVLIB Code

# **********************************************************
# IMPORT LIBRARIES
# **********************************************************
import pandas as pd

from pvlib.pvsystem import PVSystem
from pvlib import clearsky, atmosphere, solarposition, irradiance
from pvlib.location import Location
from pvlib.tracking import SingleAxisTracker
from pvlib.modelchain import ModelChain
from pvlib.temperature import TEMPERATURE_MODEL_PARAMETERS

# **********************************************************
# LOCATION & SOLAR SIZE INPUTS
# **********************************************************
# Lat and Long desired
lat = -26.18
lon = 152.63

# Set Location
tz, altitude, name = 'Australia/Queensland', 10, 'Gympie/QLD'

# Set location details for model
latitude, longitude, = lat, lon
location = Location(latitude, longitude, tz, altitude, name)

# load some module and inverter specifications
module_parameters = {'pdc0': 200000000, 'gamma_pdc': -0.004}
inverter_parameters = {'pdc': 166666666, 'pdc0': 166666666, 'eta_inv_nom': 0.96}
temperature_model_parameters = TEMPERATURE_MODEL_PARAMETERS['sapm']['open_rack_glass_glass']

# **********************************************************
# ONEBUILDING DATA
# **********************************************************
df = pd.read_csv('weather import.csv')
df['time'] = df['time'].astype('datetime64[ns]')
df.set_index(['time'], inplace=True)
df.index = df.index.tz_localize(tz=tz)
df = df.asfreq(freq='1h')
onebuilding = df

# **********************************************************
# INEICHEN CLEAR SKIES ADJUSTED FOR TURBIDITY
# **********************************************************
# Create PVLib inputs
times = df.index
solpos = solarposition.get_solarposition(times, latitude, longitude)
apparent_zenith = solpos['zenith']
rel_airmass = atmosphere.get_relative_airmass(apparent_zenith)
pressure = atmosphere.alt2pres(altitude)
abs_airmass = atmosphere.get_absolute_airmass(rel_airmass, pressure)
linke_turbidity = clearsky.lookup_linke_turbidity(times, latitude, longitude)
dni_extra = irradiance.get_extra_radiation(times)
ineichen = clearsky.ineichen(apparent_zenith, abs_airmass, linke_turbidity, altitude, dni_extra)
ineichen.to_csv('ineichen.csv')

# **********************************************************
# SELECT WHICH WEATHER DATA TO USE (ineichen v onebuilding)
# **********************************************************
# Select which version we wish to use (onebuilding, ineichen)
selected_irrad = onebuilding
print(selected_irrad)

# Create Weather File
weather = pd.DataFrame(data={'ghi': selected_irrad.ghi, 'dni': selected_irrad.dni,
                             'dhi': selected_irrad.dhi, 'temp_air': df['temp_air'],
                             'wind_speed': df['wind_speed']})

# **********************************************************
# CREATE PV SYSTEM AND PV MODEL CHAIN
# **********************************************************
# Define the specs for the PV System (fixed system)
f_system = PVSystem(
    surface_tilt=abs(lat),
    surface_azimuth=0,
    albedo=0.2,
    module='pvwatts_dc',
    inverter='pvwatts_ac',
    module_parameters=module_parameters,
    inverter_parameters=inverter_parameters,
    racking_model='open_rack_glass_glass',
    name='fixed',
    temperature_model_parameters=temperature_model_parameters
)

# Define the specs for the PV System (1 axis tracking system)
t_system = SingleAxisTracker(
    axis_tilt=0,
    axis_azimuth=0,
    max_angle=90,
    backtrack=True,
    module='pvwatts_dc',
    inverter='pvwatts_ac',
    module_parameters=module_parameters,
    inverter_parameters=inverter_parameters,
    name='tracking',
    gcr=.40,
)

# build model chain
mc = ModelChain(
    system=t_system,
    location=location,
    name='pvwatts',
    dc_model='pvwatts',
    ac_model='pvwatts',
    losses_model='pvwatts',
    aoi_model='physical',
    spectral_model='no_loss',
    temperature_model='sapm')

# run model chain
mc.run_model(weather=weather)
print(mc.ac.sum())
Bobby Heyer
  • 531
  • 5
  • 18

1 Answers1

3

Hard to say exactly why the annual energy is different without a detailed comparison of intermediate results. One contributing factor appears to be the transposition model (GHI, DHI and DNI to plane-of-array): pvlib ModelChain defaults to the Hay/Davies model, and I believe SAM defaults to the Perez 1990 model. The two models will differ by a few percent in annual plane-of-array irradiance, which varies with the relative levels of diffuse and direct irradiance; see Lave et al. Figure 6.

You can select the Perez 1990 model in pvlib by adding transposition_model = 'perez', to the mc instance. I expect that will narrow the difference between pvlib and SAM results, and am interested in what you find.

Calculations using a TMY weather file won't give the same result as a calculation using irradiance from a clear sky model, since TMY is assembled from historical weather records and so includes cloudy periods.

Cliff H
  • 314
  • 1
  • 3
  • Hi Cliff, thanks! that does help narrow the gap (increased PVLib output to 405 GWh, reducing the gap to 10 GWh p.a.). Still a bit off, but at this point, i'm gonna say close enough... As for your comment regarding Clear Sky model. I thought by including a Linke Turbidity factor/adjustment, I could approximate atmospheric absorption and scattering of the solar radiation under clear skies. In which case shouldn't it be ballpark with actuals? – Bobby Heyer Feb 17 '20 at 06:20
  • I've compared the output of different programs running the same models and usually find differences. I haven't used the PVwatts option though, so would be very interested to see how close you can get. `losses, shading & curtailment = default` seems like it could cause trouble. – adr Feb 17 '20 at 18:51
  • 1
    Hi Bobby, the Linke turbidity does account for absorption and scattering, but, from the SodaPro website: "TL denotes the transparency of the cloudless atmosphere." No shadows :) – Cliff H Feb 18 '20 at 15:06