# \[ANN\] SolarPosition.jl

**URL:** <https://discourse.julialang.org/t/ann-solarposition-jl/134263>\
**Category:** Package Announcements\
**Tags:** announcement, parallel, makie, modelingtoolkit, modelling\
**Created:** [December 1, 2025, 5:53pm UTC](https://discourse.julialang.org/t/ann-solarposition-jl/134263 "2025-12-01T17:53:10Z")\
**Posts on this page:** 5\
**Page:** 2

<div class="post-metadata">

**Author:** ![StevenSiew](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevensiew/32/218393_2.png) [@StevenSiew](https://discourse.julialang.org/u/StevenSiew)\
**Post date:** [January 6, 2026, 12:15pm UTC](https://discourse.julialang.org/t/ann-solarposition-jl/134263/21 "2026-01-06T12:15:09Z")

</div>

Can it create plots like this for the position of the sun (at 34 degrees south)  
Sunrise at 5:15 and sunset at 18:51

 ![Screenshot 2026-01-06 at 11.11.08 PM](https://global.discourse-cdn.com/julialang/original/3X/a/3/a3ab4f188e61df22202970956c5bd53b3af9ca0d.png)

---

<div class="post-metadata">

**Author:** ![GeorgeGkountouras](https://avatars.discourse-cdn.com/v4/letter/g/77aa72/32.png) [@GeorgeGkountouras](https://discourse.julialang.org/u/GeorgeGkountouras)\
**Post date:** [January 6, 2026, 12:31pm UTC](https://discourse.julialang.org/t/ann-solarposition-jl/134263/22 "2026-01-06T12:31:33Z")

</div>

Would this work with TerminalPlots.jl?

---

<div class="post-metadata">

**Author:** ![langestefan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/langestefan/32/207923_2.png) [@langestefan](https://discourse.julialang.org/u/langestefan)\
**Post date:** [January 6, 2026, 12:51pm UTC](https://discourse.julialang.org/t/ann-solarposition-jl/134263/23 "2026-01-06T12:51:45Z")

</div>

With Makie.jl you can, sure.

Here is one for drawing analemmas: [Plotting with Makie.jl · SolarPosition.jl](https://juliaastro.org/SolarPosition/stable/guides/plotting/#Polar-Coordinates-with-Hour-Labels)

 ![image](https://global.discourse-cdn.com/julialang/original/3X/a/c/ac826d9db52aa34fdf5ae678b8893e5e745b087a.jpeg)

---

<div class="post-metadata">

**Author:** ![langestefan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/langestefan/32/207923_2.png) [@langestefan](https://discourse.julialang.org/u/langestefan)\
**Post date:** [January 6, 2026, 12:54pm UTC](https://discourse.julialang.org/t/ann-solarposition-jl/134263/24 "2026-01-06T12:54:52Z")

</div>

Do you mean UnicodePlots.jl? What do you mean by ‘this’?

---

<div class="post-metadata">

**Author:** ![langestefan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/langestefan/32/207923_2.png) [@langestefan](https://discourse.julialang.org/u/langestefan)\
**Post date:** [July 31, 2026, 6:59pm UTC](https://discourse.julialang.org/t/ann-solarposition-jl/134263/25 "2026-07-31T18:59:20Z")

</div>

# [ANN] SolarPosition.jl v0.5.0

I just released [SolarPosition.jl](https://juliaregistries.github.io/General/packages/redirect_to_repo/SolarPosition) v0.5.0 😃

This release is packed with new features:

## New positioning algorithms

Two more positioning algorithms have been added, [Iqbal, 1983](https://doi.org/10.1016/B978-0-12-373750-2.X5001-0) and [Michalsky, 1988](https://doi.org/10.1016/0038-092X(88)90045-X). With that we support all the source-provided algorithms from pvlib and solposx.

| Algorithm | Reference | Accuracy | Default Refraction | Status |
| --- | --- | --- | --- | --- |
| PSA | [Blanco-Muriel et al.](https://www.sciencedirect.com/science/article/abs/pii/S0038092X00001560) | ±0.0083° | None | ✅ |
| NOAA | [Global Monitoring Laboratory](https://gml.noaa.gov/grad/solcalc/calcdetails.html) | ±0.0167° | HUGHES | ✅ |
| Walraven | [Walraven, 1978](https://doi.org/10.1016/0038-092X(78)90155-X) | ±0.0100° | None | ✅ |
| USNO | [U.S. Naval Observatory](https://aa.usno.navy.mil/faq/sun_approx) | ±0.0500° | None | ✅ |
| SPA | [Reda & Andreas, 2004](https://doi.org/10.1016/j.solener.2003.12.003) | ±0.0003° | SPA | ✅ |
| Iqbal | [Iqbal, 1983](https://doi.org/10.1016/B978-0-12-373750-2.X5001-0) | ±0.0100° | None | ✅ |
| Michalsky | [Michalsky, 1988](https://doi.org/10.1016/0038-092X(88)90045-X) | ±0.0100° | MICHALSKY | ✅ |

## Fast repeated evaluation using Interpolations.jl

We have a new extension for Interpolations.jl:

```julia
using Interpolations

alg = Interpolated(SPA(); tspan = (DateTime(2024, 1, 1), DateTime(2025, 1, 1)))
positions = solar_position(obs, times, alg)

```

This will pre-compute cubic B-splines of intermediate quantities within SPA’s algorithm, and provides an observer-agnostic interpolant that is about 10x faster to evaluate than SPA, while retaining its accuracy. This can be very convenient when you need to compute many locations and/or many time periods with decent accuracy, as you can reuse the interpolant.

## Support for `Observer{T <: Real}`

Previously all algorithms computed internal quantities at fixed `Float64`. This is now fully type-generic, meaning every algorithm now computes at the element type `T` of `Observer{T <: Real}`. Now `Float32`, `BigFloat` or even `Float128` from QuadMath.jl are all propagated properly. In fact any `T <: Real` should ‘just work’ now.

On my hardware, `Float32` yields modest speedups (10-50%). I think the biggest benefit of this is that now a type like `BigFloat` can be used to set a reference for other algorithms and implementations, even beyond Julia itself.

## Autodiff support

The generalization to `T <: Real` has also made it possible to compute derivatives with respect to observer coordinates and refraction model parameters:

```julia
import DifferentiationInterface as DI
import FiniteDiff

f(x) = solar_position(Observer(x[1], x[2]), dt, SPA(), NoRefraction()).elevation
g_ad = DI.gradient(f, DI.AutoForwardDiff(), [45.0, 10.0])
g_num = DI.gradient(f, DI.AutoFiniteDiff(), [45.0, 10.0])
(g_ad, maximum(abs.(g_ad .- g_num)))

# ([-0.9209770850903363, -0.2755706865807749], 1.9860470068522318e-7)

```

With that you can do fun things like optimize the orientation of a panel or build an axis tracker, see the docs.

_(yes I know, minimization of angle of incidence is simple trig and has an analytical solution, it’s just a demo)._

## Uncertainty propagation with Measurements.jl

The same genericity means Measurements.jl works too (awesome package!):

```julia-auto
using Measurements

pos = solar_position(Observer(52.35888 ± 0.01, 4.88185 ± 0.01), DateTime(2023, 6, 21, 12))
# SolPos(azimuth=188.399 ± 0.019°, 
# elevation=60.8792 ± 0.0099°, 
# zenith=29.1208 ± 0.0099°)

```

Correlations survive as well, so the sum of zenith and elevation has no uncertainty at all:

```julia
pos.elevation + pos.zenith
# 90.0 ± 0.0

```

With this we can answer interesting questions like, what is the impact of GPS accuracy on solar position tracking? (_To meet PSA’s ±0.0083°, the margins for azimuth and elevation are 300 m and 920 m. So coordinates are almost never the limiting factor. For SPA the requirements are about 10x stricter._)

## Sunrise and sunset at the observer’s element type

There is no obvious way to propagate uncertainty through a `DateTime`, so I added `transit_sunrise_sunset_seconds` which returns the event as seconds since midnight UTC:

```julia
transit_sunrise_sunset_seconds(Observer(52.35888 ± 0.01, 4.88185 ± 0.01), Date(2023, 6, 21))
# TransitSunriseSunset{Measurement{Float64}}(
# 42134.7 ± 2.4, 11885.5 ± 4.3, 72384.2 ± 4.3)

```

As a sidenote it also makes event times differentiable, if you ever have a need for that (I can’t think of one right now 🙂 ).

## TimeZones.jl is now an extension

`ZonedDateTime` support moved to a package extension. It still works exactly as before. You always needed to import `TimeZones` to construct a `ZonedDateTime` anyway, so in practice nothing changes. This also sets up JuliaC support for later, because currently we can’t `--trim` with TimeZones.jl.

## ModelingToolkit.jl v11 support

This release is also compatible with ModelingToolkit.jl v11. The MTK extension gives you a symbolic `SolarPositionBlock`. This `SolarPositionBlock` is a component whose azimuth, elevation and zenith are time-varying outputs you can wire into any other system:

```julia
using SolarPosition, ModelingToolkit, OrdinaryDiffEq

@named sun = SolarPositionBlock()
sys = mtkcompile(sun)

prob = ODEProblem(sys, [
    sys.observer => Observer(51.5027, -0.1778, 15.0),
    sys.t0 => DateTime(2024, 6, 21, 12),
    sys.algorithm => PSA(),
    sys.refraction => NoRefraction(),
], (0.0, 86400.0))

sol = solve(prob; saveat = 3600.0)
sol[sys.elevation]

```

There are two example usecases in the guide: composition with a solar panel power model and a building thermal model with solar gains. If you need _fast_ and accurate simulations, `SolarPositionBlock` also works beautifully with `Interpolated`!

[Previous page](https://discourse.julialang.org/t/ann-solarposition-jl/134263.md?page=1)
