Try Astrologer API

Subscribe to support and grow the project.

Known Divergences from pyswisseph #

This document catalogs all known, inherent divergences between libephemeris and pyswisseph 2.10.03. These are not bugs — they arise from fundamental differences in the underlying computation engines (Skyfield/JPL DE440 vs Swiss Ephemeris) and different implementations of secondary algorithms.

All divergences documented here have been verified through systematic hyper-validation (4400+ comparison rounds across 29 API sections).

Summary #

Category Typical Divergence Maximum Divergence Cause
Planetary positions 0.001–0.5" ~1" (Moon) Different ephemeris engines
Planetary speeds 0.01–5" ~20" (Moon speed) Numerical differentiation methods
House cusps <0.01" ~0.01" Obliquity/nutation model differences
Fixed star positions <0.01" <0.01" Proper motion catalog differences
Fixed star distances <0.01% at J2000 ~0.1% at ±50y Radial velocity models
Ayanamsha values <0.1" ~40" (exotic modes) Reference star position differences
Delta-T <0.001s (modern) ~43s (year 1900) Different delta-T polynomial models
Refraction <1" ~15" Different atmospheric models
Phase angles <1" (inner) ~18" (outer planets) Position errors amplified
Orbital elements <1" (inner) ~2000" (Jupiter) Different osculating element derivation
Sidereal Moon ~3" ~14" Lunar theory differences in sidereal frame

1. Ephemeris Engine Differences #

1.1 Planetary Positions (swe_calc_ut) #

libephemeris uses Skyfield + JPL DE440/DE441 while pyswisseph uses the Swiss Ephemeris internal integration. Both are based on JPL ephemerides but use different interpolation and integration methods.

Typical divergence:

  • Sun, Mercury–Neptune: 0.001–0.01" (sub-arcsecond)
  • Moon: 0.01–1.0" (lunar theory differences)
  • Pluto: 0.001–0.01"

Speed divergence:

  • Most planets: 0.01–2.0"
  • Moon speed: up to ~5" (different numerical differentiation)
  • With SEFLG_SPEED flag: central finite difference vs analytical

Future dates (>2050): Up to 2" position divergence due to delta-T model extrapolation differences.

1.2 Interpolated Apogee/Perigee (bodies 21, 22) #

IntpApog and IntpPerg use semi-analytical ELP2000-82B perturbation theory. libephemeris implements this independently from published coefficients, producing results that can differ by several arcseconds from the Swiss Ephemeris implementation. These bodies are classified as KNOWN divergence in all tests.

1.3 Pholus (body 16) Historical Dates #

Pholus SPK coverage in libephemeris starts around 1600 CE. Queries before this date raise “Invalid Time to evaluate” while pyswisseph may use internal Keplerian fallback. This affects dates before ~1850.

2. House System Differences #

2.1 House Cusps #

House cusps typically agree within 0.01" (sub-arcsecond). The small divergence comes from slightly different obliquity and nutation models used in the intermediate calculations.

2.2 Vertex at the Equator #

At geographic latitude 0° (equator), the Vertex calculation has a 1/tan(lat) singularity. libephemeris clamps latitude to a tiny epsilon so the formula evaluates to the correct limiting value, matching pyswisseph. The only remaining divergence is ascmc[6] (CoAsc Munkasey) for the Horizon (H) house system at lat=0, where pyswisseph returns 0° due to a C tan(90°) floating-point artifact, while the mathematical limit from any positive latitude is 180°.

2.3 House Position (swe_house_pos) #

For most house systems: <0.01" divergence.

For Alcabitius (B), Koch (K), and Topocentric (T): up to ~46" divergence due to different cusp interpolation algorithms between the engines.

3. Time and Delta-T #

3.1 Delta-T Model #

libephemeris uses Skyfield’s delta-T model (based on historical observations and IERS data) while pyswisseph uses the Espenak & Meeus (2006) polynomial model.

Period Typical Divergence Maximum
1950–2020 <0.001s ~0.01s
1900–1950 ~1s ~43s
2020–2050 <0.1s ~1s
>2050 ~1–10s depends on extrapolation

This affects all functions that internally convert between UT and ET/TT: swe_deltat, swe_deltat_ex, swe_utc_to_jd, swe_jdet_to_utc, swe_jdut1_to_utc.

3.2 Sidereal Time (swe_sidtime) #

At modern dates: <0.001s divergence. At future dates (>2050): up to ~0.05s due to delta-T propagation into the GMST calculation.

3.3 Equation of Time (swe_time_equ) #

At modern dates: <0.15s (matches well after the GAST-RA rewrite). At future dates (>2050): up to ~1.4s due to compounding delta-T and Sun RA model differences.

4. Fixed Stars #

4.1 Positions #

Ecliptic longitude and latitude agree within 0.01" for all 116 catalog stars. This is achieved by using the same FK5/Hipparcos proper motion values.

4.2 Distances #

At J2000.0 epoch: distances match exactly (same Hipparcos parallax values). At other dates: up to ~0.1% divergence due to different radial velocity models affecting the distance variation over time.

4.3 Speed in Distance (speed_dist) #

speed_dist (index 5 of the position tuple) diverges significantly (20–90%) because libephemeris computes it via central finite difference on the full apparent distance (which includes annual parallax oscillation), while pyswisseph separates the radial velocity component differently.

4.4 Magnitudes #

Star magnitudes agree within 0.5 mag for all catalog stars. Small differences arise from different catalog versions.

5. Refraction and Horizontal Coordinates #

5.1 Atmospheric Refraction (swe_refrac) #

Up to 15" divergence, primarily at low altitudes near the horizon. Both implementations use Bennett’s formula but with slightly different coefficients and boundary handling.

5.2 Azimuth/Altitude (swe_azalt) #

Above-horizon bodies: typically <1" divergence. Below-horizon bodies: up to ~1654" (~27’) divergence due to fundamentally different atmospheric refraction models for negative apparent altitudes. This is a known limitation — refraction below the horizon is physically meaningless and implementations differ in how they extrapolate.

6. Eclipse and Occultation Functions #

6.1 Solar Eclipses #

Eclipse timing (tret[0] maximum): typically <10s divergence. Eclipse geography (geopos): typically <1° divergence. Eclipse type flags may occasionally differ for borderline cases.

6.2 Lunar Eclipses #

Similar to solar eclipses. Timing agrees within ~10s for most events.

6.3 Lunar Occultations #

Timing agrees within ~0.001 day (~86s) for most events. Note: swe_lun_occult_when_glob is computationally expensive (~6s/call).

7. Nodes and Apsides (swe_nod_aps_ut) #

Nodal and apsidal positions can diverge by 20–700" (up to ~1°) for some bodies. This is because osculating orbital elements are derived differently from the two ephemeris engines. The divergence is largest for:

  • Outer planets (different integration methods)
  • Bodies with high eccentricity (osculating elements more sensitive)

8. Phenomena (swe_pheno_ut) #

8.1 Phase Angle #

Inner planets (Sun–Mars): <1" divergence. Outer planets (Jupiter–Pluto): up to ~18" divergence. The phase angle calculation amplifies the small position differences between the two engines.

8.2 Other Phenomena Values #

Phase, elongation, apparent diameter, and magnitude generally agree well (<1").

9. Orbital Elements (swe_get_orbital_elements) #

Orbital elements for inner planets (Mercury–Mars) agree within ~1".

Orbital elements for outer planets (Jupiter–Neptune) can diverge by 100–2000" in angular elements (argument of perihelion, longitude of ascending node, mean anomaly). This is because osculating elements are derived from instantaneous position and velocity vectors, and small position differences between the engines lead to large differences in the derived elements, especially for nearly circular orbits where the argument of perihelion is poorly defined.

Fictitious bodies (IntpApog=15, IntpPerg=16) have meaningless orbital elements since they are not real orbiting bodies.

10. Sidereal Calculations #

10.1 Sidereal Positions #

Most planets in sidereal mode agree within 1" (the ayanamsha subtraction is consistent).

Sidereal Moon: 3–14" divergence. The sidereal frame amplifies the underlying lunar theory differences because the ayanamsha correction interacts with nutation differently in the two engines.

10.2 Ayanamsha Values #

Standard modes (Lahiri, Fagan-Bradley, Raman): <0.1" divergence. Exotic/experimental modes: up to ~40" divergence for modes that depend on specific reference star positions or galactic frame definitions.

11. Crossing Functions #

11.1 Solar/Lunar Crossings #

swe_solcross_ut, swe_mooncross_ut: typically <1s timing divergence.

11.2 Moon Node Crossings #

swe_mooncross_node_ut: up to ~69s divergence due to different lunar node calculation methods.

12. Asteroid Pipeline (SE_AST_OFFSET) #

When using SE_AST_OFFSET + N to access asteroids by number, libephemeris remaps to dedicated body IDs and uses its Skyfield/SPK pipeline. pyswisseph uses .se1 ephemeris files with a different integration.

Typical divergence: ~0.2" for the major asteroids (Ceres, Pallas, Juno, Vesta).

Missing .se1 files: Chiron (2060) and Pholus (5145) require dedicated .se1 files in pyswisseph’s ephemeris path. If these files are not present, pyswisseph raises an error. libephemeris always has these bodies available through its SPK pipeline.

13. Heliacal Events #

swe_heliacal_ut is computationally expensive (>90s per call for some configurations). Timing divergence: up to ~2 days for heliacal rising/setting events, due to different atmospheric extinction and visibility models.

14. Constants and API #

14.1 Version String #

version intentionally differs (libephemeris reports its own version).

14.2 contrib Attribute #

pyswisseph exposes a contrib attribute (contributor credits). libephemeris does not expose this attribute.

14.3 d2l with Negative Values #

swe_d2l() for negative input values produces different results due to unsigned integer overflow behavior in the C implementation of pyswisseph vs Python’s native integer handling.

14.4 SEFLG_MOSEPH #

SEFLG_MOSEPH is accepted for API compatibility but silently ignored. All calculations always use JPL DE440/DE441 via Skyfield — there is no Moshier ephemeris fallback.

15. Gauquelin Sectors #

Gauquelin sector values typically agree within 0.01 sectors. Small divergences (<0.1) arise from the underlying planetary position and house cusp differences.

Hyper-Validation Results #

The hyper-validation script (scripts/hyper_validate.py) runs 4400+ comparison rounds across 29 sections. With all tolerance classifications applied:

Metric Count Percentage
PASS 3947 89.7%
KNOWN 441 10.0%
FAIL 0 0.0%
SKIP 12 0.3%

0 failures. All divergences are documented and classified as inherent differences between the two computation engines.

The 12 SKIP results are due to missing .se1 asteroid ephemeris files in the pyswisseph configuration (not a libephemeris limitation).

Run the hyper-validation yourself:

.venv/bin/python3 scripts/hyper_validate.py --json report.json

Exclude slow sections (occultations, heliacal) with:

.venv/bin/python3 scripts/hyper_validate.py --section A,B,C,D,E,F,G,H,I,K,L,M,N,O,P,Q,R,S,T,V,W,X,Y,Z,AA,AB,AC