Skip to main content

Library to compute astronomical bodies positions and planetary aspects between them

Project description

Ketu

PyPI version Python Versions License: MIT

Ketu is a pure NumPy library for astronomical calculations focused on planetary positions, aspects, and cycle analysis. With no dependencies beyond NumPy, Ketu provides fast, accurate calculations suitable for astrology, biodynamic calendars, and machine learning applications.

This library was originally designed to generate biodynamic calendars and time series based on astrological aspects. It can be used as a basis for building astrology software.

Terminal screen

What's New in v1.4.0

Ketu v1.4.0 introduces a dynamic harmonic aspect generator and expands the Chiron ephemeris to 1900–2100. There are two behavioural changes: Chiron now forms scored aspects (orb raised from 0° to 4°) and out-of-range Chiron inputs are silently clamped instead of raising ValueError. See UPGRADING.md for migration recipes and CHANGELOG.md for the full list of changes.

  • Dynamic harmonic generatorketu.aspects.generate_harmonic_aspects(h) builds aspect specs on the fly for any integer harmonic 2 ≤ h ≤ 64 (full-circle 360° folded to 0–180°, coef = k/h). Pass the result as dynamic_specs= to calculate_aspects, find_aspects_between_dates, or calculate_synastry. The frozen 14-row core.aspects table and preset fingerprints are byte-identical.
  • Chiron range 1900–2100ketu/data/chiron_coeffs.npz regenerated (2283 Chebyshev segments, max error 0.001214°, ~578 KB). calc_planet_position(jd, 13) now resolves any JD in the expanded range with no code changes required.
  • Chiron orb 4°core.bodies['orb'] for Chiron is now 4° (Pluto parity). Chiron forms scored aspects in all detection functions. Downstream code that assumed zero Chiron aspects must adapt (see UPGRADING.md → "v1.3 -> v1.4").

What's New in v1.3.0

Ketu v1.3.0 adds Chiron as the 14th body and makes the aspect engine data-driven. The public API is additive; the one breaking change is the internal positional-array contract (the bodies axis goes 13 → 14) and the aspect default/coefficient/preset surface. See UPGRADING.md for migration recipes and CHANGELOG.md for the full list.

  • Chiron — the 14th body (body_id = 13), evaluated from embedded Chebyshev-by-segment coefficients in pure NumPy (no pyswisseph, no SciPy, no new runtime dependency). ketu.calc_planet_position(jd, 13) returns Chiron's longitude within ~0.006° of Swiss Ephemeris across 1950–2050. Chiron participates in compute_chart, aspect detection, and the cycle machinery like any other body.
  • Data-driven aspect engine — aspects now live in a single declarative table (name, angle, coef, harmonic, symbol); the detection logic iterates over it with no per-aspect hardcoding. Compose a set from harmonics with aspects_for_harmonics([1, 2, 3, 6]), or use the CLASSICAL / TRADITIONAL / EXTENDED presets.
  • New default aspect set (breaking) — the library default is now the 7 half-circle harmonics (H1/H2/H3/H6: Conjunction, Semi-sextile, Sextile, Square, Trine, Quincunx, Opposition). The full-circle minors (quintile, novile, decile, and friends — H5/H9/H10) are opt-in. The CLI stays pinned to the classical 5 for byte-stable output.
  • Full French documentation — every Sphinx page is now fully translated to French through the gettext pipeline.

What's New in v1.2.0

Ketu v1.2.0 is a non-breaking feature release — all v1.1 code works unchanged. See UPGRADING.md for opt-in migration recipes and CHANGELOG.md for the full list of changes.

  • Synastryketu.synastry.calculate_synastry(chart_a, chart_b) returns a SYNASTRY_DTYPE cross-product of inter-chart aspects (15 bodies × 15 bodies = 225 pairs; filtered and dense modes). Astrodienst-style orbs (0.5× tightening). CLI ketu synastry; ketu --list-orbs.
  • Composite chartsketu.composite.calculate_composite(chart_a, chart_b) returns a CHART_DTYPE midpoint composite. Helper circular_midpoint(lon_a, lon_b) with pinned regression mid(359°, 1°) == 0°.
  • Solar and Lunar Returnsketu.returns.solar_return(..., target_year=<int>) and ketu.returns.lunar_return(..., target_jd=<float>) with arc-second convergence and optional relocation (return_lat/return_lon). API asymmetry: solar takes an integer year, lunar takes a Julian Date.
  • Arabic Partsketu.parts.calculate_part(name, chart) with sect-aware dispatch (Fortune / Spirit) and fixed Marriage formula; calculate_all_parts(chart) dict; ketu --list-parts.
  • Three new house systems — Whole Sign ("whole_sign"), Equal ("equal"), Regiomontanus ("regiomontanus") registered in ketu.houses.SYSTEMS. ketu --list-house-systems now returns six entries.
  • CI doc gates hardenedinterrogate ≥95% and numpydoc validate are both fully blocking; make doc-gates runs them locally. 214 pre-existing GL01 violations fixed.
  • GitHub Actions workflow refresh — Node.js 24 actions (checkout@v5, setup-python@v6, upload-artifact@v5); all Node 20 deprecation warnings eliminated.

For the full list of changes see CHANGELOG.md.

Features

  • Planetary positions for 14 bodies (Sun, Moon, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto, Rahu/Mean Node, True North Node, Lilith, Chiron)
  • Data-driven aspect engine — a declarative table of 14 major/minor aspects (Conjunction, Opposition, Trine, Square, Sextile, Quincunx, ... through Quintile, Novile, Decile); harmonic-based selection via aspects_for_harmonics([...]). The default set is the 7 half-circle aspects; full-circle minors are opt-in.
  • Aspect windows - Find when aspects begin, peak, and end
  • Transit calculations - Track transits to natal positions
  • Retrogradation detection and planet motion helpers
  • Time system conversions (UTC, Julian Day)
  • Orb system based on Abu Ma'shar (787-886) and Al-Biruni (973-1050)
  • Interactive CLI for a non-programmatic workflow
  • Python API that fits into your own tooling
  • Pure NumPy - Single dependency for maximum portability and performance

Installation

From PyPI (recommended)

pip install ketu

From source

git clone https://github.com/alkimya/ketu.git
cd ketu
pip install -e .

Quick Start

Interactive mode (CLI)

Run the command below and answer the prompts:

ketu

You will be asked for:

  • A date (ISO format: 2020-12-21)
  • A time (ISO format: 19:20)
  • A timezone (for example Europe/Paris)

The program prints:

  • Positions of every celestial body with zodiac signs
  • All inter-planet aspects with their orbs

Programmatic usage

from datetime import datetime
from zoneinfo import ZoneInfo
import ketu

# Define a datetime
dtime = datetime(2020, 12, 21, 19, 20, tzinfo=ZoneInfo("Europe/Paris"))
jday = ketu.utc_to_julian(dtime)

# Display planetary positions
ketu.print_positions(jday)

# Display aspects
ketu.print_aspects(jday)

Advanced Examples

Compute a planet position

from datetime import datetime
from zoneinfo import ZoneInfo
import ketu

dtime = datetime(2024, 10, 26, 12, 0, tzinfo=ZoneInfo("UTC"))
jday = ketu.utc_to_julian(dtime)

sun_long = ketu.long(jday, 0)
print(f"Sun longitude: {sun_long:.2f}°")

sign, deg, mins, secs = ketu.body_sign(sun_long)
print(f"Position: {ketu.signs[sign]} {deg}°{mins}'{secs}\"")

Check whether a planet is retrograde

import ketu

# Mars (body id = 4)
if ketu.is_retrograde(jday, 4):
    print("Mars is retrograde")
else:
    print("Mars is direct")

Find aspect windows

from datetime import datetime, timedelta
import ketu

# Find Sun-Moon conjunction window
start = ketu.utc_to_julian(datetime(2025, 1, 1, tzinfo=ZoneInfo("UTC")))
end = ketu.utc_to_julian(datetime(2025, 12, 31, tzinfo=ZoneInfo("UTC")))

windows = ketu.find_aspect_window(start, end, body1=0, body2=1, aspect=0)

for window in windows:
    print(f"Conjunction from {ketu.julian_to_utc(window.begin_jd)} "
          f"to {ketu.julian_to_utc(window.end_jd)}")
    print(f"  Exact: {ketu.julian_to_utc(window.exact_jd)}")

Calculate transits to natal positions

import ketu

# Natal positions
natal_date = ketu.utc_to_julian(datetime(1990, 1, 15, 12, 0, tzinfo=ZoneInfo("UTC")))
natal_positions = ketu.get_natal_positions(natal_date)

# Find transits for a specific date
transit_date = ketu.utc_to_julian(datetime(2025, 11, 22, 12, 0, tzinfo=ZoneInfo("UTC")))
transits = ketu.compare_dates_transits(natal_positions, transit_date)

for transit in transits:
    print(f"{transit.transiting_body} {transit.aspect} natal {transit.natal_body}")

Ephemeris Cache (v0.4.0)

For ML pipelines and high-frequency lookups, use the ephemeris cache for 1000x faster position lookups:

from ketu.cache import EphemerisCache
from datetime import datetime, timezone

# Initialize cache (stores in ~/.ketu/ephemeris_cache/)
cache = EphemerisCache()

# Pre-compute a range of months (one-time operation)
# ~1-2 seconds per month, persisted to disk
for year in range(2020, 2026):
    for month in range(1, 13):
        cache.ensure_month(year, month)

# Fast O(1) lookups (0.006ms vs 10ms computation)
timestamp = datetime(2025, 6, 15, 14, 30, tzinfo=timezone.utc)

# Get single body position (lon, lat, distance, speed)
sun_pos = cache.get_position(timestamp, body_id=0)
print(f"Sun longitude: {sun_pos[0]:.2f}°")

# Get all 14 bodies at once
all_positions = cache.get_all_positions(timestamp)
# Returns dict: {body_id: (lon, lat, dist, speed), ...}

CLI for pre-computing cache:

# Pre-compute 2020-2030 (takes ~3-4 minutes)
python scripts/precompute_ephemeris.py --years 2020-2030

# Single year
python scripts/precompute_ephemeris.py --year 2025

# Force recompute
python scripts/precompute_ephemeris.py --year 2025 --force

Performance:

  • Lookup: 0.006ms (with interpolation)
  • Compute: 10ms
  • Speedup: 1000x
  • Disk usage: ~50KB per month

Documentation

The full documentation is hosted on Read the Docs.

Included sections:

  • Installation: detailed setup instructions
  • Quickstart: guided tour of the basics
  • Concepts: astrological and astronomical background
  • API Reference: all functions documented
  • Examples: advanced usage patterns
  • Developer Guide: architecture and performance details

Documentation Quality Gates

Documentation quality is enforced by CI on every push:

  • interrogate ≥95% (blocking) — every public function, class, and module has a docstring.
  • numpydoc validate (warning, blocking from v1.2.0) — docstrings follow the NumPy convention.

Run both locally before pushing: make doc-gates.

Requirements

  • Python 3.10 or higher
  • numpy ≥ 1.20.0 — numerical routines and arrays

That's it! Ketu has no other dependencies.

Supported bodies

Body ID Orb Average speed (°/day)
Sun 0 12° 0.986
Moon 1 12° 13.176
Mercury 2 1.383
Venus 3 10° 1.200
Mars 4 0.524
Jupiter 5 10° 0.083
Saturn 6 10° 0.034
Uranus 7 0.012
Neptune 8 0.007
Pluto 9 0.004
Rahu (Mean Node) 10 -0.013
True North Node 11 -0.013
Lilith (Black Moon) 12 -0.113
Chiron 13 0.019

Supported aspects

Aspect Angle Orb coefficient
Conjunction 1
Semi-sextile 30° 1/6
Sextile 60° 1/3
Square 90° 1/2
Trine 120° 2/3
Quincunx 150° 5/6
Opposition 180° 1

These 7 half-circle aspects (harmonics 1, 2, 3, 6) are the library default. The full table also carries 7 full-circle minor aspects (Quintile, Decile, Novile, Binovile, Quadrinovile, Biquintile, Tredecile — harmonics 5, 9, 10), which are opt-in via aspects_for_harmonics([...]) or the EXTENDED preset.

Performance

The pure NumPy implementation provides excellent performance:

  • Time series (365 days): 208x faster than loop-based approach
  • Aspect calculations: 14.55x faster with vectorization
  • Single planet position: 67x faster with optimized algorithms
  • Moon position: 59x faster with custom perturbation calculations

See docs/en/performance.md for detailed benchmarks.

Accuracy

The implementation provides good accuracy for astrological purposes:

  • Planetary positions: ±0.1° for inner planets, ±0.5° for outer planets
  • Moon position: ±0.5° (includes major perturbations)
  • Aspect timing: ±2 minutes for exact aspects
  • Best accuracy range: 1800-2200 CE

Architecture

ketu/
├── __init__.py          # Main API
├── core.py              # Data structures (bodies, aspects, signs)
├── calculations.py      # High-level calculation functions
├── complex.py           # Complex-number engine for cycle analysis
├── display.py           # Display utilities
├── lunar_calendar.py    # Biodynamic / lunar calendar helpers
├── aspects/             # Data-driven aspect engine (presets, harmonics)
├── charts/              # compute_chart / CHART_DTYPE abstraction
├── houses/              # Six house systems (Placidus, Whole Sign, ...)
├── synastry/            # Inter-chart aspect cross-products
├── composite/           # Midpoint composite charts
├── returns/             # Solar and lunar returns
├── parts/               # Arabic Parts framework
├── cycles/              # Planetary cycle series (NumPy structured arrays)
├── cli/                 # Interactive command-line interface
├── cache/               # High-performance ephemeris cache
├── data/                # Embedded Chiron Chebyshev coefficients (.npz)
└── ephemeris/           # Astronomical calculations
    ├── time.py          # Time conversions
    ├── orbital.py       # Orbital mechanics (re-export hub)
    ├── coordinates.py   # Coordinate transformations
    ├── planets.py       # Planetary position calculations (per-body strategies)
    └── chiron.py        # Pure-NumPy Chiron Chebyshev evaluator

Roadmap

  • Removal of dependency on pyswisseph
  • Pure numpy implementation of planetary calculations
  • Search for exact aspects between two dates
  • Aspect windows and timing
  • Transit calculations
  • High-performance ephemeris cache
  • Complex number engine for cycle analysis
  • Configurable aspects and six house systems
  • Chart abstraction (compute_chart / CHART_DTYPE)
  • Relational charts (synastry, midpoint composite)
  • Predictive charts (solar and lunar returns)
  • Arabic Parts framework
  • Chiron as the 14th body (pure-NumPy Chebyshev evaluation)
  • Data-driven aspect engine with harmonic-based selection

Contribution

Contributions are welcome! Feel free to:

  • Open an issue to report a bug or suggest a feature
  • Submit a pull request
  • Improve the documentation

See CONTRIBUTING.md for more details.

License

This project is licensed under MIT. See the LICENSE file for more details.

Contact

Loc Cosnier - @alkimya

Project: https://github.com/alkimya/ketu

Acknowledgments

  • solarsystem by Ioannis Nasios — The pure Python astronomy library that inspired and served as the mathematical foundation for Ketu's NumPy ephemeris engine. Kepler's equation solver, perturbation terms, coordinate transformations, and Moon calculations all trace back to this elegant, dependency-free library. Thank you!
  • Claude by Anthropic — The pure NumPy rewrite, from orbital mechanics to aspect detection, was developed in collaboration with Claude. Architecture, algorithms, tests, documentation were produced through extensive pair programming sessions.
  • GSD (Get Shit Done) — The project management workflow that structured the development of Ketu v1.0.0 into phases with research, planning, execution, and verification steps.
  • Original orbital calculations based on Paul Schlyter's work
  • Inspired by the accuracy and reliability of Swiss Ephemeris
  • Built with the power of NumPy for scientific computing

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ketu-1.4.0.tar.gz (985.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ketu-1.4.0-py3-none-any.whl (792.4 kB view details)

Uploaded Python 3

File details

Details for the file ketu-1.4.0.tar.gz.

File metadata

  • Download URL: ketu-1.4.0.tar.gz
  • Upload date:
  • Size: 985.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ketu-1.4.0.tar.gz
Algorithm Hash digest
SHA256 a8389f8f833116cfa316f7cedf468aa44809656736b5d2cb45ec6920104ca250
MD5 3de4c4564bfa74e48f5c54feab480a36
BLAKE2b-256 abbf1e8b38a657fe9f376734fba7b867d593012a147f0a8d264097d2c4bc9f42

See more details on using hashes here.

Provenance

The following attestation bundles were made for ketu-1.4.0.tar.gz:

Publisher: publish.yml on alkimya/ketu

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ketu-1.4.0-py3-none-any.whl.

File metadata

  • Download URL: ketu-1.4.0-py3-none-any.whl
  • Upload date:
  • Size: 792.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ketu-1.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 017738dc141ec5b3fc0040f3bbb44606cf75fedfe9fa3faa2a311167a8d6cdd1
MD5 4f5eee7868573c1fc5729afd1361d819
BLAKE2b-256 7cd1036d1c5cc6f75d67257622ff1d63407cc75751a3fc2483188c26230b14d3

See more details on using hashes here.

Provenance

The following attestation bundles were made for ketu-1.4.0-py3-none-any.whl:

Publisher: publish.yml on alkimya/ketu

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page