Kepler Solvers

The heart of this package is being able to take a set of Keplerian elements and output relative positions, velocities, etc.

This normaly requires solving Kepler's equation numerically. This package supports a multitude of solver algorithms that can be passed to orbitsolve:

The last of these RootsMethod, allows one to substitute any algorithm from the Roots.jl package. These include many different classical and modern root finding algorithms.chosen precision, including artibrary precision BigFloats. Using big floats with, for example, Roots.PlanetOrbits.Thukral5B and a tight tolerenace, allows you to solve orbits up to arbitrary precision.

The default choice is Auto, which currently selects Markley for all cases. The Markley algorithm is very fast, reasonably accurate, and always converges, making it a good default choice.

The Markley algorithm is a tweaked version of the algorithm from AstroLib.jl. It is non-iterative and converges with less than 1e-15 relative error across the full range of e between 0 and 1. On my laptop, this solves for a single eccentric anomaly in just 71 ns. Since it is implemented in pure Julia, there is no overhead from calling into a C or Cython compiled function and no need for vectorization.

Examples

using PlanetOrbits, BenchmarkTools
orb = orbit(a=1.2, e=0.1, M=1.0, ω=1.4, τ=0.5)
t = mjd("2025-06-23")
@benchmark orbitsolve(orb, t, PlanetOrbits.Markley())
BenchmarkTools.Trial: 10000 samples with 550 evaluations.
 Range (minmax):  209.211 ns 20.581 μs   GC (min … max): 0.00% … 98.85%
 Time  (median):     215.276 ns                GC (median):    0.00%
 Time  (mean ± σ):   248.313 ns ± 454.402 ns   GC (mean ± σ):  5.08% ±  2.78%

  ██▃          ▃▅▅▆▆▆▅▄▃▂▁           ▂
  ███▇█▆▆▇▅▄▄▄▄▇██▇▇▆▄▄▃▄▅▄▅▅▅▄▁▃▃▃▃▁▁▃▃████████████▇▇██▇▇███ █
  209 ns        Histogram: log(frequency) by time        281 ns <

 Memory estimate: 128 bytes, allocs estimate: 1.
@benchmark orbitsolve(orb, t, PlanetOrbits.Goat())
BenchmarkTools.Trial: 10000 samples with 159 evaluations.
 Range (minmax):  662.811 ns 50.226 μs   GC (min … max): 0.00% … 98.29%
 Time  (median):     726.836 ns                GC (median):    0.00%
 Time  (mean ± σ):   720.678 ns ± 690.826 ns   GC (mean ± σ):  1.35% ±  1.39%

   █     ▁▅                     ▂                             
  ▄█▃▃▂▂▂██▃▃▂▂▂▂▂▂▂▂▁▂▂▁▂▂▂▃█▅▄██▅▄▃▄▃▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂ ▃
  663 ns           Histogram: frequency by time          800 ns <

 Memory estimate: 128 bytes, allocs estimate: 1.
using Roots
@benchmark orbitsolve(orb, t, PlanetOrbits.RootsMethod(Roots.Newton()))
BenchmarkTools.Trial: 10000 samples with 334 evaluations.
 Range (minmax):  263.159 ns 28.713 μs   GC (min … max): 0.00% … 98.54%
 Time  (median):     309.189 ns                GC (median):    0.00%
 Time  (mean ± σ):   306.051 ns ± 534.370 ns   GC (mean ± σ):  3.88% ±  2.21%

  █▆▁                       ▅▅▄▆▅▄▄▃▂▁                       ▂
  ███▇▆█▄▅▃▄▁▁▃▄▄▃▇█▇▆▅▆▆▃▃▁██████████▆█▇▇▇▇▆█▇▇▇▇▆▆▄▆▆▅▄▄▃▄ █
  263 ns        Histogram: log(frequency) by time        365 ns <

 Memory estimate: 128 bytes, allocs estimate: 1.
using Roots
@benchmark orbitsolve(orb, t, PlanetOrbits.RootsMethod(Roots.Thukral3B()))
BenchmarkTools.Trial: 10000 samples with 251 evaluations.
 Range (minmax):  296.131 ns 31.054 μs   GC (min … max): 0.00% … 99.01%
 Time  (median):     345.709 ns                GC (median):    0.00%
 Time  (mean ± σ):   339.626 ns ± 529.790 ns   GC (mean ± σ):  2.70% ±  1.71%

  ▁█▇▃▁                      ▂ ▁▆▄▄▆▆▄▄▄▄▂▁▁                  ▂
  █████▆▅▇▇▅▁▁▄▁▃▁▁▅▁▄▁▁▁▁▇██████████████████▆▆▆▆▆▅▇▇▇▆▇▆▇▅▆▇ █
  296 ns        Histogram: log(frequency) by time        392 ns <

 Memory estimate: 128 bytes, allocs estimate: 1.
@benchmark orbitsolve(orb, t, PlanetOrbits.RootsMethod(Roots.A42()))
BenchmarkTools.Trial: 10000 samples with 116 evaluations.
 Range (minmax):  758.060 ns 1.173 μs   GC (min … max): 0.00% … 0.00%
 Time  (median):     812.897 ns               GC (median):    0.00%
 Time  (mean ± σ):   803.607 ns ± 34.451 ns   GC (mean ± σ):  0.00% ± 0.00%

  ▅█▄▂▁         ▁▆▆▁▂▇▄▃▃▆▆▃▂▂▂▂                             ▂
  █████▅▆▆▆▇▆▃▃▁█████████████████▇▇▇▆▆▇▅▆▆▅▆▇█▆▇▆▇█▆▇▇▆▇▇▆▇▇ █
  758 ns        Histogram: log(frequency) by time       919 ns <

 Memory estimate: 128 bytes, allocs estimate: 1.
@benchmark orbitsolve(orb, t, PlanetOrbits.RootsMethod(Roots.Bisection()))
BenchmarkTools.Trial: 10000 samples with 10 evaluations.
 Range (minmax):  1.297 μs  3.846 μs   GC (min … max): 0.00% … 0.00%
 Time  (median):     1.304 μs                GC (median):    0.00%
 Time  (mean ± σ):   1.366 μs ± 123.671 ns   GC (mean ± σ):  0.00% ± 0.00%

  █                       ▂▅▅▄▃▂▁                    ▁▁      ▂
  ██▇▆▆▆▆▆▅▅▃▇▅▁▁▁▁▁▁▁▁▁▄████████▆▆▆▅▄▅▅▆▅▄▄▄▄▄▄▃▅▆▇███▇▇▆▆ █
  1.3 μs       Histogram: log(frequency) by time      1.66 μs <

 Memory estimate: 128 bytes, allocs estimate: 1.
@benchmark orbitsolve(orb, t, PlanetOrbits.RootsMethod(Roots.SuperHalley()))
BenchmarkTools.Trial: 10000 samples with 328 evaluations.
 Range (minmax):  263.360 ns 24.337 μs   GC (min … max): 0.00% … 98.74%
 Time  (median):     311.649 ns                GC (median):    0.00%
 Time  (mean ± σ):   307.826 ns ± 531.856 ns   GC (mean ± σ):  3.86% ±  2.21%

  ▆█▅▂                           ▄▄▅▆▅▅▆▅▄▄▃▂▁▁                ▂
  ████▇▇█▆▄▄▄▁▃▁▄▃▁▁▁▅█▇▅▆▃▃▄▃▄▃██████████████▇▇▇▇▇▇▇▇▆▆▇▇▆▇▇ █
  263 ns        Histogram: log(frequency) by time        353 ns <

 Memory estimate: 128 bytes, allocs estimate: 1.
@benchmark orbitsolve(orb, t, PlanetOrbits.RootsMethod(Roots.Brent()))
BenchmarkTools.Trial: 10000 samples with 8 evaluations.
 Range (minmax):  3.832 μs  6.243 μs   GC (min … max): 0.00% … 0.00%
 Time  (median):     3.865 μs                GC (median):    0.00%
 Time  (mean ± σ):   3.938 μs ± 162.489 ns   GC (mean ± σ):  0.00% ± 0.00%

  ▅█▄▄▄▃▂▁   ▂▅▅▃▃▂▂▁       ▁                               ▂
  ████████▆▆▆██████████▇▆▅▇████▇▇▆▆▄▄▃▄▅▁▃▃▃▄▄▃▃▃▄▄▅▁▁▄▁▁▁▄ █
  3.83 μs      Histogram: log(frequency) by time      4.73 μs <

 Memory estimate: 128 bytes, allocs estimate: 1.
@benchmark orbitsolve(orb, t, PlanetOrbits.RootsMethod(Roots.Order2()))
BenchmarkTools.Trial: 10000 samples with 221 evaluations.
 Range (minmax):  332.072 ns 36.422 μs   GC (min … max): 0.00% … 98.94%
 Time  (median):     380.443 ns                GC (median):    0.00%
 Time  (mean ± σ):   375.588 ns ± 619.999 ns   GC (mean ± σ):  2.85% ±  1.71%

  ██                                                             
  ██▃▃▂▂▂▂▂▂▂▂▁▁▁▁▂▁▁▁▂▁▂▂▂▂▂█▄▃▇█▄▃▄▄▃▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂ ▃
  332 ns           Histogram: frequency by time          435 ns <

 Memory estimate: 128 bytes, allocs estimate: 1.
@benchmark orbitsolve(orb, t, PlanetOrbits.RootsMethod(Roots.AlefeldPotraShi()))
BenchmarkTools.Trial: 10000 samples with 147 evaluations.
 Range (minmax):  697.565 ns 54.075 μs   GC (min … max): 0.00% … 98.64%
 Time  (median):     746.231 ns                GC (median):    0.00%
 Time  (mean ± σ):   742.369 ns ± 534.281 ns   GC (mean ± σ):  0.72% ±  0.99%

  ▆█▆▃▁               ▄▆▅▇▆▄▂▄▆▅▃▂▂▂▂▁                       ▂
  █████▅▆▆▇▇▅▅▃▁▃▃▃▁▁▁██████████████████▇▇▆▅▆▇▆▆▆▇▇▇▆▅▇█▇▇▆▇ █
  698 ns        Histogram: log(frequency) by time        824 ns <

 Memory estimate: 128 bytes, allocs estimate: 1.

High precision

You can solve Kepler's equation in high precision using big floats and tightening the tolerance on the solver.

orb_big = orbit(a=big(1.2), e=big(0.1), M=big(1.0), ω=big(1.4), τ=big(0.5))
sol = orbitsolve(orb_big, big(t), PlanetOrbits.RootsMethod(Roots.Thukral5B(),rtol=1e-30,atol=1e-30,))
radvel(sol)
25030.34323194365565831507041947847102607642627168092403020601399008600937133855

Comparison