Notes on NREL NSRDB PSM4

52 views
Skip to first unread message

Will Hobbs

unread,
Apr 24, 2025, 1:15:53 PMApr 24
to pvlib-python
If you had not seen it, there are several variations of the NREL NSRDB PSM v4 now available: https://developer.nrel.gov/docs/solar/nsrdb/. pvlib.iotools has been updated to access PSM4 and those changes will be available in a future release. See https://github.com/pvlib/pvlib-python/issues/2326 and https://github.com/pvlib/pvlib-python/pull/2378

Notable differences: PSM4 includes 2023 (PSM3 ends at 2022) and appears to fix the "horns" issue explored in https://doi.org/10.1109/PVSC57443.2024.10748762 and elsewhere (see https://github.com/pvlib/pvlib-python/issues/2326#issuecomment-2577927005 for an illustration). 

I've just run some rough tests, and it looks like PSM4 can result in annual AC energy 1-3% lower than PSM3 (spot checking Birmingham, AL, and Denver, CO).

This is obviously not a rigorous comparison, but I thought folks here might find it interesting and/or get some ideas.

Will

den.png

bhm.png

Code:

import pvlib
import matplotlib.pyplot as plt

# NREL api info
api_key = 'DEMO_KEY' # <-- change this
email = 'your...@email.com' # <-- change this

def simple_backtracing_power(resource,nameplate_dc,nameplate_ac,
                             eta_inv_nom,gamma_pdc,dc_loss_fraction,
                             gcr,max_angle):
   
    # get solar position
    times = resource.index
    solar_position = pvlib.solarposition.get_solarposition(times, latitude, longitude)

    # array orientation
    orientation = pvlib.tracking.singleaxis(
        solar_position['apparent_zenith'],
        solar_position['azimuth'],
        max_angle=max_angle,
        backtrack=True,
        gcr=gcr,
    )

    # poa irradiance
    poa = pvlib.irradiance.get_total_irradiance(
            surface_tilt=orientation.surface_tilt,
            surface_azimuth=orientation.surface_azimuth,
            dni=resource['dni'],
            ghi=resource['ghi'],
            dhi=resource['dhi'],
            solar_zenith=solar_position['apparent_zenith'],
            solar_azimuth=solar_position['azimuth'])

    # cell temperature
    temp_cell = pvlib.temperature.faiman(
        poa_global=poa['poa_global'],
        temp_air=resource['temp_air'],
        wind_speed=resource['wind_speed'],
    )

    # dc power
    pdc = pvlib.pvsystem.pvwatts_dc(
        g_poa_effective=poa['poa_global'],
        temp_cell=temp_cell,
        pdc0=nameplate_dc,
        gamma_pdc=gamma_pdc,
        )
    pdc_inv = pdc * (1 - dc_loss_fraction)  # dc power into the inverter after losses

    # ac power
    pdc0 = nameplate_ac/eta_inv_nom
    power_ac = pvlib.inverter.pvwatts(pdc_inv, pdc0, eta_inv_nom)

    return power_ac

nameplate_dc = 135
nameplate_ac = 100
eta_inv_nom = 0.98
gamma_pdc = -0.0035
dc_loss_fraction = 0.1
gcr = 0.35
max_angle = 60
names_list = list(map(str,range(1998,2022+1)))

# first location
latitude, longitude = 33.5, -86.8
result=[]
for names in names_list:
    # get PSM3 resource data
    resource, metadata = pvlib.iotools.get_psm3(latitude, longitude, api_key, email, names, interval=30)

    # get PSM4 resource data
    resource4, metadata4 = pvlib.iotools.get_nsrdb_psm4_aggregated(
        latitude, longitude, api_key, email, year=names, time_step=30) # note that input parameter for year is now "year"


    power_ac_psm3 = simple_backtracing_power(resource,nameplate_dc,nameplate_ac,
                                eta_inv_nom,gamma_pdc,dc_loss_fraction,
                                gcr,max_angle)

    power_ac_psm4 = simple_backtracing_power(resource4,nameplate_dc,nameplate_ac,
                                eta_inv_nom,gamma_pdc,dc_loss_fraction,
                                gcr,max_angle)
   
    diff = (power_ac_psm3.sum()-power_ac_psm4.sum())/power_ac_psm3.sum()
    result.append(diff)

plt.bar(names_list,result)
plt.gcf().autofmt_xdate()
plt.ylabel('Relative Difference in Annual AC Energy, \n (psm3 - psm4)/psm3')
plt.title('BHM')
plt.show()

# second location
latitude, longitude = 39.7, -105.0
result=[]
for names in names_list:
    # get PSM3 resource data
    resource, metadata = pvlib.iotools.get_psm3(latitude, longitude, api_key, email, names, interval=30)

    # get PSM4 resource data
    resource4, metadata4 = pvlib.iotools.get_nsrdb_psm4_aggregated(
        latitude, longitude, api_key, email, year=names, time_step=30) # note that input parameter for year is now "year"


    power_ac_psm3 = simple_backtracing_power(resource,nameplate_dc,nameplate_ac,
                                eta_inv_nom,gamma_pdc,dc_loss_fraction,
                                gcr,max_angle)

    power_ac_psm4 = simple_backtracing_power(resource4,nameplate_dc,nameplate_ac,
                                eta_inv_nom,gamma_pdc,dc_loss_fraction,
                                gcr,max_angle)
   
    diff = (power_ac_psm3.sum()-power_ac_psm4.sum())/power_ac_psm3.sum()
    result.append(diff)

plt.bar(names_list,result)
plt.gcf().autofmt_xdate()
plt.ylabel('Relative Difference in Annual AC Energy, \n (psm3 - psm4)/psm3')
plt.title('DEN')
plt.show()

Reply all
Reply to author
Forward
0 new messages