Migration Guide

This guide helps you upgrade Ketu across major and minor versions. For the full list of changes, see the Changelog.


Upgrading from v1.2 to v1.3

Breaking Change: CHART_DTYPE body axis expanded from 13 to 14 bodies (D-08)

In v1.3, Chiron is added as body_id=13 (the 14th body). This change expands several array dimensions:

Before (v1.2):

  • chart["body_lons"] — shape (13,)

  • chart["body_lats"] — shape (13,)

  • chart["body_speeds"] — shape (13,)

  • chart["aspect_matrix"] — shape (13, 13)

  • chart["aspect_orbs"] — shape (13, 13)

  • SYNASTRY_BODY_COUNT = 15 (13 bodies + ASC + MC)

After (v1.3):

  • chart["body_lons"] — shape (14,) — index 13 = Chiron

  • chart["body_lats"] — shape (14,)

  • chart["body_speeds"] — shape (14,)

  • chart["aspect_matrix"] — shape (14, 14)

  • chart["aspect_orbs"] — shape (14, 14)

  • SYNASTRY_BODY_COUNT = 16 (14 bodies + ASC + MC)

Migration actions:

  1. Code that iterates range(13) over body arrays: update to range(14) or use len(chart["body_lons"]).

  2. Cached CHART_DTYPE arrays from v1.2: these are incompatible. Recompute them with v1.3.

  3. Downstream consumers checking body count: update any hardcoded 13 or SYNASTRY_BODY_COUNT == 15 assertions.

  4. Index 12 (previously last body = Lilith): remains unchanged — Chiron is index 13.

# Before (v1.2) — iterating over bodies
for i in range(13):
    lon = chart["body_lons"][i]

# After (v1.3) — use len() or the constant 14
for i in range(len(chart["body_lons"])):   # 14 bodies
    lon = chart["body_lons"][i]
    # i == 13 is Chiron (new)

New: Chiron access

No new import needed — use existing calculation functions with body_id=13:

from ketu.calculations import long, lat

chiron_lon = long(jd, 13)   # Chiron longitude
chiron_lat = lat(jd, 13)    # Chiron latitude

Valid date range: 1900–2100 (expanded in v1.4). Out-of-range input is silently clamped to the nearest segment boundary — no ValueError is raised.


Upgrading from v1.1 to v1.2

New Subpackages

v1.2 adds five new subpackages. No existing API was removed or renamed.

ketu.charts — full natal chart:

from ketu.charts import compute_chart, is_day_chart, CHART_DTYPE

chart = compute_chart(jd, lat=48.8566, lon=2.3522, system="placidus")
# chart["body_lons"][0] — Sun longitude
# chart["asc"]          — Ascendant

ketu.synastry — inter-chart aspects:

from ketu.synastry import calculate_synastry, SYNASTRY_DTYPE

syn = calculate_synastry(chart_a, chart_b)

ketu.composite — midpoint composite:

from ketu.composite import calculate_composite, circular_midpoint

comp = calculate_composite(chart_a, chart_b)

ketu.returns — solar and lunar returns:

from ketu.returns import solar_return, lunar_return

sr = solar_return(natal_jd, natal_lat, natal_lon, target_year=2026)
lr = lunar_return(natal_jd, natal_lat, natal_lon, target_jd=search_jd)

ketu.parts — Arabic Parts:

from ketu.parts import calculate_part, calculate_all_parts

fortune = calculate_part("fortune", chart)

Additional House Systems

Three new systems added to ketu.houses:

  • "whole_sign" — each house = one full zodiac sign

  • "equal" — 30° equal divisions from ASC

  • "regiomontanus" — celestial equator division

from ketu.houses import calculate_houses

h = calculate_houses(jd, lat, lon, system="whole_sign")

Upgrading from v1.0 to v1.1

New: Configurable Aspect Sets

calculate_aspects now accepts an optional aspects parameter. When introduced in v1.1 the default was EXTENDED (all 14 aspects). As of v1.3 the library default is TRADITIONAL (7 half-circle aspects); see Aspects for the preset table.

from ketu.aspects import calculate_aspects, CLASSICAL, TRADITIONAL, EXTENDED

# v1.1 default at the time (EXTENDED — all 14 aspects; the current v1.3+ default is TRADITIONAL)
all_aspects = calculate_aspects(jd)

# New v1.1: filter to classical only
classical = calculate_aspects(jd, aspects=CLASSICAL)

New: House Systems

ketu.houses is a new subpackage — no existing code is broken.

from ketu.houses import calculate_houses, house_of, SYSTEMS

h = calculate_houses(jd, lat=48.8566, lon=2.3522, system="placidus")
print(f"ASC: {h['asc']:.2f}°")

# Determine which house a planet is in
from ketu.calculations import long
sun_house = house_of(long(jd, 0), h["cusps"])

Import Path Corrections (if upgrading from pre-1.0)

If you were using the old import ketu; ketu.long(...) style (which was never officially supported as a public API), switch to the correct submodule imports:

# Old (never worked correctly — ketu.long is not in ketu.__all__)
import ketu
lon = ketu.long(jd, 0)

# Correct (v1.0+)
from ketu.calculations import long
lon = long(jd, 0)

Upgrading from v0.4.0 to v1.0.0

Removed: Export Modules

Ketu 1.0 is a pure calculation library. Visualization and calendar export features have been removed:

  • Removed modules: ketu.export.chart, ketu.export.icalendar

  • Removed functions: draw_zodiacal_chart(), export_lunations_to_ical(), export_aspects_to_ical(), export_transits_to_ical()

Migration: Implement visualization in your application layer using Ketu’s calculation results.

Removed: Pandas Dependency

  • generate_aspect_timeline() now returns NumPy structured array (was DataFrame)

  • AspectTimeline.to_pandas() removed

  • Migration: Use import pandas as pd; df = pd.DataFrame(timeline) for manual conversion

Renamed: Velocity Functions (Breaking)

  • vlong()long_velocity()

  • vlat()lat_velocity()

  • vdist_au()dist_velocity_au()

Migration: Use find-and-replace in your codebase:

sed -i 's/ketu\.vlong(/ketu.long_velocity(/g' *.py
sed -i 's/ketu\.vlat(/ketu.lat_velocity(/g' *.py
sed -i 's/ketu\.vdist_au(/ketu.dist_velocity_au(/g' *.py

Changed: Public API Surface

  • ketu.__init__.py exports only metadata + core constants

  • Functions accessed via submodule imports: from ketu.calculations import long

  • ketu.__all__ explicitly lists public API

Migration: Most users won’t notice this change. If you were importing from internal modules, switch to public API imports.

Correctness Fixes (v0.4.0 → v1.0.0)

IMPORTANT: These fixes change calculation results. Recompute cached 0.4.0 results.

  1. Cache operator precedence bug: use_cache=False was ignored due to missing parentheses

  2. Aspect vectorization non-determinism: calculate_aspects_vectorized() now returns consistent results

  3. Moon velocity wrapping: Correct velocity at 360°/0° boundary (was showing ±360° spikes)

Migration Steps (v0.4.0 → v1.0.0)

Step 1: Update Package

pip install --upgrade ketu

Step 2: Update Your Code

Replace velocity function calls:

# Before (v0.4.0)
v = ketu.vlong(jday, body_id)

# After (v1.0.0)
from ketu.calculations import long_velocity
v = long_velocity(jday, body_id)

Replace pandas conversions:

# Before (v0.4.0)
timeline = ketu.generate_aspect_timeline(...)
df = timeline.to_pandas()

# After (v1.0.0)
import pandas as pd
timeline = ketu.generate_aspect_timeline(...)
df = pd.DataFrame(timeline)

Remove chart/icalendar calls:

# Before (v0.4.0) - REMOVED
ketu.draw_zodiacal_chart(jday, output_file="chart.svg")
ketu.export_lunations_to_ical(start, end, "lunations.ics")

# After (v1.0.0) - No replacement
# Implement visualization in your application layer using ketu calculation results

Step 3: Test Your Code

Run your test suite to catch any remaining issues.

Step 4: Recompute Cached Results

If you cached calculation results from 0.4.0, recompute them for correctness.

Rollback

Versions 0.3.0 and 0.4.0 were not published to PyPI, so pip install ketu==0.4.0 will not work. If you need the previous behavior, install from source:

git clone https://github.com/alkimya/ketu.git
cd ketu
git checkout v0.2.1  # Last published pre-NumPy version
pip install .

Note: v1.0.0 is the first NumPy-based release on PyPI. The previous PyPI version is v0.2.1 (pyswisseph-based).

Getting Help

If you encounter issues:

  1. Review the Changelog for complete list of changes

  2. Open an issue