pvlib / pvlib-python

A set of documented functions for simulating the performance of photovoltaic energy systems.
https://pvlib-python.readthedocs.io
BSD 3-Clause "New" or "Revised" License
1.16k stars 980 forks source link

`spa_python` with `numba` fails with array input #2166

Open markveillette opened 1 month ago

markveillette commented 1 month ago

Describe the bug I'm trying to call pvlib.solarposition.spa_python with arrays for time, latitudes and longitudes. It works fine by default, but I get an error if I use how='numba' with the same inputs.

To Reproduce The following reproduces my error:

import pandas as pd
import numpy as np
import pvlib

N = 100
dates = pd.date_range(start='2020-01-01',end='2050-01-01',periods=N)
lats = np.linspace(-60,60,N)
lons = np.linspace(-80,80,N)

# works fine
print('Testing without numba')
pvlib.solarposition.spa_python(dates, latitude=lats, longitude=lons)
print('worked!')

# fails
print('Testing with numba')
pvlib.solarposition.spa_python(dates, latitude=lats, longitude=lons, how='numba')

The error I get is

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[1], line 17
     15 # fails
     16 print('Testing with numba')
---> 17 pvlib.solarposition.spa_python(dates, latitude=lats, longitude=lons, how='numba')

File ..../lib/python3.11/site-packages/pvlib/solarposition.py:385, in spa_python(time, latitude, longitude, altitude, pressure, temperature, delta_t, atmos_refract, how, numthreads)
    380 spa = _spa_python_import(how)
    382 delta_t = delta_t or spa.calculate_deltat(time.year, time.month)
    384 app_zenith, zenith, app_elevation, elevation, azimuth, eot = \
--> 385     spa.solar_position(unixtime, lat, lon, elev, pressure, temperature,
    386                        delta_t, atmos_refract, numthreads)
    388 result = pd.DataFrame({'apparent_zenith': app_zenith, 'zenith': zenith,
    389                        'apparent_elevation': app_elevation,
    390                        'elevation': elevation, 'azimuth': azimuth,
    391                        'equation_of_time': eot},
    392                       index=time)
    394 return result

File .../lib/python3.11/site-packages/pvlib/spa.py:1088, in solar_position(unixtime, lat, lon, elev, pressure, temp, delta_t, atmos_refract, numthreads, sst, esd)
   1085 else:
   1086     do_calc = solar_position_numpy
-> 1088 result = do_calc(unixtime, lat, lon, elev, pressure,
   1089                  temp, delta_t, atmos_refract, numthreads,
   1090                  sst, esd)
   1092 if not isinstance(result, np.ndarray):
   1093     try:

File .../python3.11/site-packages/pvlib/spa.py:923, in solar_position_numba(unixtime, lat, lon, elev, pressure, temp, delta_t, atmos_refract, numthreads, sst, esd)
    919 """Calculate the solar position using the numba compiled functions
    920 and multiple threads. Very slow if functions are not numba compiled.
    921 """
    922 # these args are the same for each thread
--> 923 loc_args = np.array([lat, lon, elev, pressure, temp, delta_t,
    924                      atmos_refract, sst, esd])
    926 # construct dims x ulength array to put the results in
    927 ulength = unixtime.shape[0]

ValueError: setting an array element with a sequence. The requested array has an inhomogeneous shape after 1 dimensions. The detected shape was (9,) + inhomogeneous part.

Expected behavior Both calls to spa_python should give same result, given the inputs are identical.

Versions:

kandersolar commented 1 month ago

I'm inclined to consider this a feature request rather than a bug report since spa_python's docstring specifies that latitude and longitude are of type float (not array). Arrays being allowed with how='numpy' is coincidental :)

It seems possible to modify the numba code to allow arrays for lat/lon, although the code might be a little ugly.

@markveillette do you need specifically the SPA for your application, or would a faster (but somewhat less accurate) solar position algorithm be acceptable?

markveillette commented 1 month ago

fair enough! That makes total sense, appreciate you looking at this so quickly.

For my use case, yes, I am okay with fast-but-not-perfectly-accurate. e.g. <1 degree error.

I've found a some repos online that offer this, e.g. https://github.com/david-salac/Fast-SZA-and-SAA-computation/tree/master or @leaver2000 's https://github.com/leaver2000/fast_spa. If you have other alternatives that handle array inputs I can definitely look into them.

AdamRJensen commented 1 month ago

SG2 was developed for a mesh of latitudes and longitudes: https://github.com/gschwind/sg2

Works for python < 3.12

kandersolar commented 1 month ago

Another option in pvlib is pvlib.solarposition.ephemeris. I'd expect it to work with array input for lat/lon, although also coincidentally.

A future version of pvlib may have even better alternatives.