Skip to main content

xalen

Pure-Rust astronomical & astrology ephemeris for Python — VSOP87 planets, Vedic/Western/KP, house systems, ayanamsa, fixed stars, numerology. A drop-in for pyswisseph, with no AGPL and no data files to ship.

PyPI CI License

Part of the XALEN Ephemeris suite — pure-Rust core, zero unsafe in the math, thread-safe, Apache-2.0. The Python wheel is the xalen-python crate (PyO3) wrapping that core.


Install

pip install xalen

To build from the repository instead:

pip install maturin
git clone https://github.com/vedika-io/xalen-ephemeris.git
cd xalen-ephemeris/crates/xalen-python
maturin develop --release        # builds + installs into the active venv
# or build a wheel:  maturin build --release  ->  target/wheels/xalen-*.whl

maturin enables the extension-module Cargo feature automatically (it is set in pyproject.toml), so the wheel resolves Python symbols against the host interpreter — no libpython link needed.

Requires Python ≥ 3.8.

Platform support

Wheels are built with the abi3 stable ABI (abi3-py38), so a single wheel per platform imports unchanged on CPython 3.8 and every later 3.x — there is no per-interpreter-version wheel to pick.

Platform On PyPI
Linux x86_64, glibc ≥ 2.17 (manylinux2014) cp38-abi3 wheel
Linux aarch64, glibc ≥ 2.17 (manylinux2014) cp38-abi3 wheel
macOS arm64, 11.0 or later cp38-abi3 wheel
macOS x86_64, 10.12 or later cp38-abi3 wheel
Windows, musl Linux and any other platform sdist: pip install builds from source (needs a Rust toolchain)

The wheels and the sdist are built on one Linux host by scripts/build-release-artifacts.sh, which also smoke-tests them. A Windows wheel is not published yet.


import xalen.swe as swe — the pyswisseph drop-in

The headline feature: migrating an existing pyswisseph (a.k.a. swisseph) codebase is a one-line search-and-replace.

# before
import swisseph as swe
# after
import xalen.swe as swe

Every function mirrors the shape of the matching pyswisseph entry point — argument order, tuple layout, and the SE_* / SEFLG_* / SIDM_* constants (exposed under both the bare pyswisseph spellings and the SE_-prefixed ones).

import xalen.swe as swe

jd = swe.julday(1990, 6, 15, 10.5)            # -> Julian Day (UT1)

# Sun position with speed. Returns ((lon, lat, dist, lon_speed, lat_speed,
# dist_speed), ret_flag) — exactly like pyswisseph.
xx, retflag = swe.calc_ut(jd, swe.SUN, swe.FLG_SWIEPH | swe.FLG_SPEED)
lon, lat, dist, lon_speed, *_ = xx

# Sidereal (Lahiri) longitude.
swe.set_sid_mode(swe.SIDM_LAHIRI, 0.0, 0.0)
sid, _ = swe.calc_ut(jd, swe.SUN, swe.FLG_SWIEPH | swe.FLG_SIDEREAL)

# Houses: (cusps[12], ascmc[8]). ascmc = (asc, mc, armc, vertex,
# equatorial_ascendant, co_ascendant_koch, co_ascendant_munkasey, polar_ascendant).
cusps, ascmc = swe.houses_ex(jd, 18.52, 73.85, b"P")   # Placidus (hsys as bytes)
asc, mc = ascmc[0], ascmc[1]
# Sidereal houses: pass SEFLG_SIDEREAL (active-mode ayanamsa subtracted).
sid_cusps, sid_ascmc = swe.houses_ex(jd, 18.52, 73.85, b"P", swe.FLG_SIDEREAL)

# Ayanamsa, ΔT, calendar round-trip, fixed stars.
aya  = swe.get_ayanamsa_ut(jd)
dt   = swe.deltat(jd)
y, m, d, h = swe.revjul(jd)
star_xx, star_name, _ = swe.fixstar2_ut("Aldebaran", jd, swe.FLG_SWIEPH)
mag, _ = swe.fixstar2_mag("Aldebaran")

Honest compatibility scope

XALEN is a faithful shape drop-in, not a byte-for-byte clone. The differences are deliberate and documented:

  • Speeds (xx[3..6]) are 0.0 unless SEFLG_SPEED is in the flags — matching Swiss.
  • houses_ex returns ascmc of length 8; all eight slots are populated — [0..4] are asc, mc, armc, vertex and [4..8] are the equatorial ascendant, the Koch and Munkasey co-ascendants, and the polar ascendant (validated against pyswisseph's swe.houses_armc auxiliary points). hsys may be passed as bytes (b"P", the pyswisseph form) or str ("P"). Passing SEFLG_SIDEREAL in flags returns the sidereal frame (active-mode ayanamsa subtracted from every cusp and angle; ARMC, a sidereal-time angle, is unchanged).
  • swe.calc(jd_et, ...) takes a TT/ET epoch (like pyswisseph) and converts it to UT1 via ΔT before computing, so swe.calc(jd_tt, ...) equals swe.calc_ut(jd_tt − deltat(jd_tt), ...). (The compat core still works in UT1; calc does the ΔT bridge for you.)
  • Position-altering flags XALEN does not yet implement (SEFLG_HELCTR, SEFLG_TOPOCTR, SEFLG_J2000, SEFLG_EQUATORIAL, SEFLG_BARYCTR, SEFLG_XYZ, SEFLG_RADIANS) raise ValueError rather than silently returning a geocentric ecliptic position mislabeled as something else. A loud error beats a silent-wrong drop-in.
  • set_ephe_path() and close() are no-ops — XALEN embeds all data at compile time, so there are no ephemeris files to point at or resources to release.

See docs/MIGRATING_FROM_SWISS_EPH.md for the full migration guide.


Native xalen API (richer, structured returns)

When you are not porting Swiss code, the native xalen module gives you dicts instead of positional tuples.

planet_position — the full 6-tuple + retrograde

The high-fidelity counterpart to planet_longitude. Equivalent to Swiss calc_ut(..., FLG_SPEED) plus a retrograde flag:

import xalen

jd = xalen.julian_day(2000, 1, 1, 12.0)        # J2000.0
p = xalen.planet_position(jd, 0)               # body 0 = Sun, tropical
# {
#   "longitude": 280.37, "latitude": 0.0, "distance": 0.9833,   # AU
#   "lon_speed": 1.0194, "lat_speed": ..., "dist_speed": ...,   # per day
#   "is_retrograde": False,
# }

# Sidereal (Lahiri = ayanamsa 0): sidereal longitude AND the ayanamsa's own
# precession rate is removed from lon_speed (matches Swiss SIDEREAL|SPEED).
p_sid = xalen.planet_position(jd, 0, sidereal=True, ayanamsa=0)

# Ketu (id 13) = Rahu + 180°, sharing Rahu's speed/retrograde.
ketu = xalen.planet_position(jd, 13)

longitude, latitude, and the speeds are in degrees (speeds per day); distance/dist_speed are in AU. longitude is wrapped to [0, 360); is_retrograde is taken from the tropical longitude rate regardless of the sidereal flag.

Body IDs

0=Sun · 1=Moon · 2=Mercury · 3=Venus · 4=Mars · 5=Jupiter · 6=Saturn · 7=Uranus · 8=Neptune · 9=MeanNode (Rahu) · 10=TrueNode · 11=Pluto · 12=Chiron · 13=Ketu (Rahu+180).

Other native functions

import xalen

jd = xalen.julian_day(1990, 6, 15, 10.5)

xalen.planet_longitude(jd, 1, sidereal=True, ayanamsa=0)  # just the longitude (float)
xalen.all_planets(jd, sidereal=True, ayanamsa=0)          # {"Sun":..,"Moon":.., "Ketu":..}

# Full Vedic chart: 9 grahas (+Ketu) with nakshatra/pada/rashi/lord, plus
# Whole-Sign ascendant, MC, ayanamsa_deg, and the 12 cusps.
chart = xalen.full_chart(jd, 18.52, 73.85, ayanamsa=0)
print(chart["planets"]["Sun"])     # {"longitude":.., "nakshatra":.., "pada":.., "rashi":.., "lord":..}
print(chart["ascendant"], chart["mc"], chart["ayanamsa_deg"])

# Panchang (five limbs) for a JD.
pan = xalen.panchang(jd, ayanamsa=0)
# {"tithi": {"number":..,"name":..,"paksha":"Shukla"|"Krishna"},
#  "nakshatra": "...", "yoga": {"number":..,"name":..}, "karana": "...", "vara": "..."}

# Nakshatra detail from a sidereal Moon longitude.
nak = xalen.nakshatra(123.45)      # {"name":.., "pada":.., "lord":.., "deity":.., "index":..}
xalen.rashi(123.45)                # e.g. "Simha (Leo)"

# Houses across 14 systems (0=WholeSign .. 13=Krusinski).
h = xalen.houses(jd, 18.52, 73.85, system=2)  # Placidus
# {"cusps":[12], "ascendant":.., "mc":.., "ic":.., "descendant":.., "vertex":..}

# Ayanamsa value (17 systems, 0=Lahiri).
xalen.ayanamsa(jd, system=0)
xalen.delta_t(jd)                  # ΔT (TT−UT1) seconds, SMH 2016 model
xalen.fixed_star_conjunctions(123.45, 1.0, 2000.0)

# Numerology.
xalen.life_path(1990, 6, 15)
xalen.expression_number("Ada Lovelace", "pythagorean")  # or "chaldean"

# String-named convenience variants.
xalen.planet_longitude_by_name("moon", jd)
xalen.sidereal_longitude("moon", jd, "lahiri")
xalen.houses_by_name(jd, 18.52, 73.85, "placidus")
xalen.ayanamsa_by_name(jd, "lahiri")

Accepted name strings: bodies sun, moon, mercury, venus, earth, mars, jupiter, saturn, uranus, neptune, pluto, rahu/mean_node, true_node, chiron, lilith/mean_apogee. Ayanamsas lahiri, kp, raman, fagan-bradley, true-chitra, true-revati, surya-siddhanta, yukteswar, jn-bhasin, deluce, ushashashi, pushya-paksha, galactic-center, lahiri-icrc, kp-straight-line, hipparchos, lahiri-vp285. House systems whole-sign, equal, placidus, koch, porphyry, regiomontanus, campanus, morinus, alcabitius, topocentric, meridian, vehlow, sripati, krusinski-pisa (dashes/spaces ignored, case-insensitive).


Accuracy — what is and is not claimed

XALEN is cross-validated against JPL Horizons (DE440), the real DE440 binary kernel, the official VSOP87 check file, Swiss Ephemeris (swetest), and public calculators. The honest framing: XALEN matches these references to the bounds below — DE440 is the reference; we do not "beat" it. The differentiator is pure Rust, zero unsafe core, thread-safe, Apache-2.0, no data files.

Body Theory Measured error vs JPL DE440 (analytical engine)
Sun, Mercury–Saturn VSOP87A + IAU 2000B nutation sub-arcsecond (Sun 0.21″, Mercury–Saturn ≤ 0.76″; 20k-chart bound)
Uranus, Neptune VSOP87A + nutation Uranus 1.78″, Neptune 2.53″ (20k-chart bound)
Moon ELP2000-82 (Meeus, 60+60 terms) ~3″ RMS (max ~12″), AD 1600–2100
Pluto Meeus Goffin 43-term fit ~1 arcminute, 1885–2099
Rahu/Ketu (Mean Node) Analytical mean model exact (mean model)

vs Swiss Ephemeris: 0 of 5,000,000 charts over 0.1° for any planet or node (most < 0.02°), worldwide, 1850–2150. Loading the optional de440s.bsp kernel gives JPL-grade sub-arcsecond on the Sun + planets and full-range Pluto. Full report: docs/ACCURACY.md.

Which Swiss flags are / are not supported

Supported (shape + behavior) Not supported — raises ValueError
SEFLG_SWIEPH (always the active backend) SEFLG_HELCTR (heliocentric)
SEFLG_SPEED (daily motion) SEFLG_TOPOCTR (topocentric)
SEFLG_SIDEREAL (with set_sid_mode) SEFLG_J2000 (J2000 frame)
SEFLG_EQUATORIAL (RA/Dec)
SEFLG_BARYCTR (barycentric)
SEFLG_XYZ (cartesian)
SEFLG_RADIANS

All these constants are still defined on xalen.swe (so existing import lines and flags = ... arithmetic work); only their position-altering effect is unimplemented, and passing one to calc_ut raises rather than silently returning the wrong frame.


Editor autocomplete

Type stubs ship with the wheel (xalen.pyi, xalen/swe.pyi), so pyswisseph and native users both get full autocomplete and type checking in editors and mypy.


License

Apache-2.0. See LICENSE and CREDITS.md.

Metadata

Release files for xalen 0.7.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for xalen 0.7.0
File Size Uploaded Provenance Actions
xalen-0.7.0.tar.gz 1.1 MB Without provenance Details for xalen-0.7.0.tar.gz

Built distributions (wheels)

Table of built distributions (wheels) for xalen 0.7.0
File Actions
xalen-0.7.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl CPython 3.8 abi3 Linux glibc 2.17+ x86-64 Details for xalen-0.7.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
xalen-0.7.0-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl CPython 3.8 abi3 Linux glibc 2.17+ ARM64 Details for xalen-0.7.0-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
xalen-0.7.0-cp38-abi3-macosx_11_0_arm64.whl CPython 3.8 abi3 macOS 11.0+ ARM64 Details for xalen-0.7.0-cp38-abi3-macosx_11_0_arm64.whl
xalen-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl CPython 3.8 abi3 macOS 10.12+ x86-64 Details for xalen-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl

Total release size: 7.0 MB

Release files / xalen-0.7.0.tar.gz

Download URL xalen-0.7.0.tar.gz
Size 1.1 MB
Tags Source
SHA-256 checksum
How to use checksums
50c866204bd4707fe26f4d053028049452e4765565f7a96a255b8cff581ecc48
BLAKE2b-256 checksum
How to use checksums
8d581221e4ca9b965dd1ed9ea87b39b4e412af09fb7a74ad7e7580746cbed288
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.10

Release files / xalen-0.7.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl

Download URL xalen-0.7.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
Size 1.5 MB
Tags CPython 3.8 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
d0916ff5c19f70bef2b6ed46acc125cc41830795725659f0a05715134ea430aa
BLAKE2b-256 checksum
How to use checksums
8b072bad30ac87146def06b47595eeeb56d62e9ac35e21d74c0f92b31edfdf7b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.10

Release files / xalen-0.7.0-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl

Download URL xalen-0.7.0-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
Size 1.4 MB
Tags CPython 3.8 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
1e511799384cd1f19c9d084eb4adae0cb8aff7d76fa3382fa2b9362b3f0e99dc
BLAKE2b-256 checksum
How to use checksums
eeff8f3e91eb88bf99e5e27b1b18bfeb468c711ea8fe36afeb57388cb52f8dc3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.10

Release files / xalen-0.7.0-cp38-abi3-macosx_11_0_arm64.whl

Download URL xalen-0.7.0-cp38-abi3-macosx_11_0_arm64.whl
Size 1.5 MB
Tags CPython 3.8 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
6392853392fd0473be9c968e72aed8d398287e6a838ad2ccb3b0481b7f7f57af
BLAKE2b-256 checksum
How to use checksums
b0e8225058b391be62dc1952ac58ea13f728a5b9b5a93cac2402fc7e47a21fc4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.10

Release files / xalen-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl

Download URL xalen-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl
Size 1.5 MB
Tags CPython 3.8 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
8540caf1c7545b2aa8bd00e6e3b6cdedb8a735bdc43a37496af3adb462e38d7d
BLAKE2b-256 checksum
How to use checksums
4cd77d4fc2e0f6b4922315ec6941162f3686846b27fb8666aafbec7befec374f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.10

Security for xalen 0.7.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page