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 626 evaluations.
 Range (minmax):  193.877 ns 17.662 μs   GC (min … max): 0.00% … 98.65%
 Time  (median):     198.422 ns                GC (median):    0.00%
 Time  (mean ± σ):   231.545 ns ± 402.007 ns   GC (mean ± σ):  5.11% ±  2.95%

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

 Memory estimate: 128 bytes, allocs estimate: 1.
@benchmark orbitsolve(orb, t, PlanetOrbits.Goat())
BenchmarkTools.Trial: 10000 samples with 163 evaluations.
 Range (minmax):  659.577 ns 45.950 μs   GC (min … max): 0.00% … 98.52%
 Time  (median):     710.528 ns                GC (median):    0.00%
 Time  (mean ± σ):   708.504 ns ± 640.549 ns   GC (mean ± σ):  1.28% ±  1.39%

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

 Memory estimate: 128 bytes, allocs estimate: 1.
using Roots
@benchmark orbitsolve(orb, t, PlanetOrbits.RootsMethod(Roots.Newton()))
BenchmarkTools.Trial: 10000 samples with 394 evaluations.
 Range (minmax):  245.000 ns 19.502 μs   GC (min … max): 0.00% … 98.41%
 Time  (median):     293.301 ns                GC (median):    0.00%
 Time  (mean ± σ):   287.706 ns ± 461.935 ns   GC (mean ± σ):  3.92% ±  2.41%

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

 Memory estimate: 128 bytes, allocs estimate: 1.
using Roots
@benchmark orbitsolve(orb, t, PlanetOrbits.RootsMethod(Roots.Thukral3B()))
BenchmarkTools.Trial: 10000 samples with 298 evaluations.
 Range (minmax):  276.658 ns 26.427 μs   GC (min … max): 0.00% … 98.83%
 Time  (median):     323.945 ns                GC (median):    0.00%
 Time  (mean ± σ):   318.779 ns ± 519.526 ns   GC (mean ± σ):  3.26% ±  1.98%

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

 Memory estimate: 128 bytes, allocs estimate: 1.
@benchmark orbitsolve(orb, t, PlanetOrbits.RootsMethod(Roots.A42()))
BenchmarkTools.Trial: 10000 samples with 106 evaluations.
 Range (minmax):  775.792 ns 75.566 μs   GC (min … max): 0.00% … 98.89%
 Time  (median):     824.179 ns                GC (median):    0.00%
 Time  (mean ± σ):   827.691 ns ± 748.277 ns   GC (mean ± σ):  0.90% ±  0.99%

  ▄█▆▂▂            ▂▇▁▄▇▆▄▂▂▂▄▄▁ ▁▁                          ▂
  ██████▅▅▂▆▆▅▄▃▃▂▂█████████████████▇▆▅▄▆█▆▇▅▅▆▆▆▅▆▇█▆▆▅▆▆▅▅ █
  776 ns        Histogram: log(frequency) by time        929 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.302 μs  3.620 μs   GC (min … max): 0.00% … 0.00%
 Time  (median):     1.312 μs                GC (median):    0.00%
 Time  (mean ± σ):   1.372 μs ± 124.219 ns   GC (mean ± σ):  0.00% ± 0.00%

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

 Memory estimate: 128 bytes, allocs estimate: 1.
@benchmark orbitsolve(orb, t, PlanetOrbits.RootsMethod(Roots.SuperHalley()))
BenchmarkTools.Trial: 10000 samples with 379 evaluations.
 Range (minmax):  246.715 ns 21.068 μs   GC (min … max): 0.00% … 98.69%
 Time  (median):     294.931 ns                GC (median):    0.00%
 Time  (mean ± σ):   286.049 ns ± 412.018 ns   GC (mean ± σ):  2.87% ±  1.97%

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

 Memory estimate: 128 bytes, allocs estimate: 1.
@benchmark orbitsolve(orb, t, PlanetOrbits.RootsMethod(Roots.Brent()))
BenchmarkTools.Trial: 10000 samples with 194 evaluations.
 Range (minmax):  495.768 ns 41.319 μs   GC (min … max): 0.00% … 98.70%
 Time  (median):     545.711 ns                GC (median):    0.00%
 Time  (mean ± σ):   546.317 ns ± 699.474 ns   GC (mean ± σ):  2.22% ±  1.71%

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

 Memory estimate: 128 bytes, allocs estimate: 1.
@benchmark orbitsolve(orb, t, PlanetOrbits.RootsMethod(Roots.Order2()))
BenchmarkTools.Trial: 10000 samples with 237 evaluations.
 Range (minmax):  314.557 ns 33.439 μs   GC (min … max): 0.00% … 99.03%
 Time  (median):     360.502 ns                GC (median):    0.00%
 Time  (mean ± σ):   356.455 ns ± 564.099 ns   GC (mean ± σ):  2.74% ±  1.71%

   █                                                             
  ▆█▅▃▂▂▂▂▂▂▂▂▁▂▂▂▂▂▁▁▂▂▂▁▂▂▂▃▃▄▆▄▃▄▅▃▃▃▃▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂ ▂
  315 ns           Histogram: frequency by time          410 ns <

 Memory estimate: 128 bytes, allocs estimate: 1.
@benchmark orbitsolve(orb, t, PlanetOrbits.RootsMethod(Roots.AlefeldPotraShi()))
BenchmarkTools.Trial: 10000 samples with 153 evaluations.
 Range (minmax):  679.438 ns 52.432 μs   GC (min … max): 0.00% … 98.65%
 Time  (median):     730.974 ns                GC (median):    0.00%
 Time  (mean ± σ):   727.917 ns ± 723.331 ns   GC (mean ± σ):  1.40% ±  1.40%

  ▆█▅▃▂               ▅▃▁▁▅▄▄▅▆▄▃▃▃▂▁                         ▂
  █████▆▅▆█▇▆▄▄▃▄▄▁▁▁█████████████████▇██▇▆▅▇██▆▇▆█▇▆▇█▇▆▆▇▇▇ █
  679 ns        Histogram: log(frequency) by time        805 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)
24650.95447361446997022436542900109713321175234563222583018047585031821959181256

Comparison