SolarPosition.jl provides a simple, unified interface to a collection of validated solar position
algorithms written in pure, performant julia.
Solar positioning algorithms are commonly used to calculate the solar zenith and azimuth angles, which are essential for various applications where the sun is important, such as:
- Solar energy systems
- Building design
- Climate studies
- Astronomy
This package is based on the work done by researchers in the field of solar photovoltaics in the packages solposx and pvlib-python. In particular the positioning and refraction methods have been adapted from solposx, while the SPA algorithm and the deltat calculation are ported from pvlib-python. These packages also provide validation data necessary to ensure correctness of the algorithm implementations.
julia> using SolarPosition, Dates, TimeZones
# define observer location (latitude, longitude, altitude in meters)
julia> obs = Observer(52.35888, 4.88185, 100.0) # Van Gogh Museum, Amsterdam
Observer(latitude=52.35888°, longitude=4.88185°, altitude=100.0m)
julia> tz = TimeZone("Europe/Brussels")
Europe/Brussels (UTC+1/UTC+2)
# a few hours of timestamps
julia> times = collect(DateTime(2023, 6, 21, 10):Hour(1):DateTime(2023, 6, 21, 15));
# compute solar positions for all timestamps
julia> positions = solar_position(obs, times)
6-element StructArray(::Vector{Float64}, ::Vector{Float64}, ::Vector{Float64}) with eltype SolPos{Float64}:
SolPos(azimuth=136.1908215897601°, elevation=55.13208390809107°, zenith=34.86791609190893°)
SolPos(azimuth=160.3753655770986°, elevation=59.974081481305134°, zenith=30.025918518694862°)
SolPos(azimuth=188.3992597996431°, elevation=60.87918930278924°, zenith=29.120810697210757°)
SolPos(azimuth=214.62987222053295°, elevation=57.493462259959394°, zenith=32.5065377400406°)
SolPos(azimuth=235.5258846451899°, elevation=50.992647293443966°, zenith=39.007352706556034°)
SolPos(azimuth=251.77304757136397°, elevation=42.790197455865076°, zenith=47.209802544134924°)Calculate sunrise, sunset, and solar noon for a specific date with timezone:
julia> result = transit_sunrise_sunset(obs, ZonedDateTime(2023, 6, 21, tz))
TransitSunriseSunset{ZonedDateTime}(
transit=2023-06-21T13:42:15+02:00,
sunrise=2023-06-21T05:18:05+02:00,
sunset=2023-06-21T22:06:24+02:00
)Find the next sunrise from a specific time in UTC:
julia> next_sunrise(obs, DateTime(2023, 6, 21, 12, 30))
2023-06-22T03:18:19Find the next sunset in UTC:
julia> next_sunset(obs, DateTime(2023, 6, 21, 12, 30))
2023-06-21T20:06:24SolarPosition.jl implements most of the solar position algorithms commonly found in
literature and industrial applications. If your preferred algorithm is not listed here,
please open an issue or submit a pull request.
| Algorithm | Reference | Accuracy | Default Refraction |
|---|---|---|---|
| SPA | Reda & Andreas, 2004 | ±0.0003° | SPA |
| PSA | Blanco-Muriel et al. | ±0.0083° | None |
| Walraven | Walraven, 1978 | ±0.0100° | None |
| Iqbal | Iqbal, 1983 | ±0.0100° | None |
| Michalsky | Michalsky, 1988 | ±0.0100° | MICHALSKY |
| NOAA | Global Monitoring Laboratory | ±0.0167° | HUGHES |
| USNO | U.S. Naval Observatory | ±0.0500° | None |
For dense time series, the Interpolated wrapper precomputes cubic B-splines of SPA's
geocentric solar coordinates and reconstructs positions analytically, roughly 10× faster
per query while maintaining SPA's accuracy. Conveniently the same interpolant can be reused
for every new observer. It's provided through a package extension for
Interpolations.jl.
using Interpolations
alg = Interpolated(SPA(); tspan = (DateTime(2024, 1, 1), DateTime(2025, 1, 1)))
positions = solar_position(obs, times, alg)See the interpolation guide for accuracy figures and when the construction cost pays off.
All algorithms are generic over the number type, so solar positions are differentiable with ForwardDiff.jl out of the box, with no extension package needed:
using ForwardDiff
grad = ForwardDiff.gradient(
x -> solar_position(Observer(x[1], x[2]), dt).elevation,
[45.0, 10.0],
)The autodiff guide shows gradients through refraction models, panel orientation optimization, and a single axis tracker example.
Atmospheric refraction correction algorithms available in SolarPosition.jl.
| Algorithm | Reference | Atmospheric Parameters |
|---|---|---|
| HUGHES | Hughes, 1985 | Pressure, Temperature |
| ARCHER | Archer et al., 1980 | None |
| BENNETT | Bennett, 1982 | Pressure, Temperature |
| MICHALSKY | Michalsky, 1988 | None |
| SG2 | Blanc & Wald, 2012 | Pressure, Temperature |
| SPARefraction | Reda & Andreas, 2004 | Pressure, Temperature |
SolarPosition.jl provides optional extensions that are automatically loaded when you have the corresponding package installed.
| Extension | Trigger Package | Features |
|---|---|---|
| Makie | Makie.jl |
Plotting recipes for solar position visualization |
| OhMyThreads | OhMyThreads.jl |
Parallel computation of solar positions |
| ModelingToolkit | ModelingToolkit.jl |
Symbolic solar position models for simulations |
| Interpolations | Interpolations.jl |
Fast Interpolated algorithm construction |
| TimeZones | TimeZones.jl |
ZonedDateTime input and zoned sunrise/sunset |
The computation runs at the precision of the Observer{T} element type. Float32,
Float64, Float128 from Quadmath.jl, and BigFloat are all supported. The
precision guide lists
the measured accuracy and runtime of every algorithm at every precision we tested.
If you use SolarPosition.jl in your work, please cite using the reference given in CITATION.cff.
If you want to make contributions of any kind, please first that a look into our contributing guide directly on GitHub or the contributing page on the website