`spa_python` with `numba` fails with array input
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:
-
pvlib.__version__: 0.11.0 -
pandas.__version__: 2.2.2 - python:3.11
- numba : 0.60.0
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?
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.
SG2 was developed for a mesh of latitudes and longitudes: https://github.com/gschwind/sg2
Works for python < 3.12
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.