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:24Here we provide an overview of the solar positioning algorithms currently implemented in SolarPosition.jl. Each algorithm is described with its reference paper, claimed accuracy and implementation status.
| Algorithm | Reference | Accuracy | Default Refraction | Status |
|---|---|---|---|---|
| PSA | Blanco-Muriel et al. | ±0.0083° | None | ✅ |
| NOAA | Global Monitoring Laboratory | ±0.0167° | HUGHES | ✅ |
| Walraven | Walraven, 1978 | ±0.0100° | None | ✅ |
| USNO | U.S. Naval Observatory | ±0.0500° | None | ✅ |
| SPA | Reda & Andreas, 2004 | ±0.0003° | Built-in | ✅ |
The computation runs at the precision of the Observer{T} element type, so
Observer{Float32}(45.0, 10.0) computes in Float32 and Observer{BigFloat}(45.0, 10.0)
computes in BigFloat. A magnitude-safe time base keeps full intra-day resolution at
every precision instead of riding on the ~2.45e6 Julian Date. Float64 is the default
and the reference. Float32 trades a little accuracy for a modest speedup and stays
within the algorithms' own claimed accuracy. Float128 from Quadmath.jl and BigFloat
give genuine extended precision at a large runtime cost. Float16 is not supported
because the algorithm coefficients overflow its range.
The table below lists the maximum error against a 256-bit BigFloat reference and the
runtime relative to Float64, measured over a grid of dates from 2015 to 2035 and
latitudes from 70°N to 60°S:
| Algorithm | Float32 error |
Float32 runtime |
Float64 error |
Float128 error |
Float128 runtime |
BigFloat runtime |
|---|---|---|---|---|---|---|
| PSA | 0.011° | 1.3× faster | 1.3e-11° | 7.1e-30° | 93× slower | 500× slower |
| NOAA | 0.0019° | 1.2× faster | 3.5e-12° | broken1 | n/a | 330× slower |
| Walraven | 0.0040° | 1.3× faster | 6.4e-12° | 8.6e-30° | 74× slower | 400× slower |
| USNO | 0.0036° | 1.2× faster | 9.5e-12° | broken1 | n/a | 300× slower |
| SPA | 0.012° | 1.5× faster | 1.8e-11° | 1.1e-29° | 118× slower | 330× slower |
Atmospheric refraction correction algorithms available in SolarPosition.jl.
| Algorithm | Reference | Atmospheric Parameters | Status |
|---|---|---|---|
| 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 | ✅ |
| SPA | Reda & Andreas, 2004 | Pressure, Temperature | ✅ |
SolarPosition.jl provides optional extensions that are automatically loaded when you import the corresponding packages:
| 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 |
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