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 = Chironchart["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:
Code that iterates
range(13)over body arrays: update torange(14)or uselen(chart["body_lons"]).Cached
CHART_DTYPEarrays from v1.2: these are incompatible. Recompute them with v1.3.Downstream consumers checking body count: update any hardcoded
13orSYNASTRY_BODY_COUNT == 15assertions.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.icalendarRemoved 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()removedMigration: 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__.pyexports only metadata + core constantsFunctions accessed via submodule imports:
from ketu.calculations import longketu.__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.
Cache operator precedence bug:
use_cache=Falsewas ignored due to missing parenthesesAspect vectorization non-determinism:
calculate_aspects_vectorized()now returns consistent resultsMoon 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: