Changelog
All notable changes to Ketu are documented here.
The format follows Keep a Changelog and adheres to Semantic Versioning.
[1.6.0] - 2026-06-04
Added 1.6.0
ketu.declinationsubpackage — declination aspects (parallels & contra-parallels): a NEW additive subpackage detecting parallel (P) and contra-parallel (CP) aspects on the equatorial declination axis (δ), independent of ecliptic longitude. (Phase 36)find_declination_aspects(body_decl): scalar/single-chart detector. Takes the(14,)signed-δchart["body_decl"]array; returns aDECLA_ASPECT_DTYPEstructured array (upper-triangle pairs, sorted, deduplicated);np.empty(0, …)when none detected (neverNone).declination_aspect_masks(body_decl): vectorized batch path. Accepts(S, 14)or(14,)(promoted vianp.atleast_2d); returns aDeclinationAspectMasksNamedTuple of(S, 91)masks +(91,)index/orb vectors. Pure broadcasting, no Python body loop.DeclinationAspectMasksNamedTuple (6 fields:parallel,contra,gap,idx_i,idx_j,orb_pairs).DECLA_ASPECT_DTYPE(5 fields:body1,body2,kind∈ {“P”,“CP”},gap,orb).DECLA_COEF = 1/12andMIN_DECL_ORB = 0.5°: the body-derived orb formulamax((orb_b1 + orb_b2)/2 × DECLA_COEF, MIN_DECL_ORB)→ Sun/Moon = 1.0°, zero-orb bodies (Rahu/Ketu/Lilith) floored to 0.5°.
Notes 1.6.0
CHART_DTYPEunchanged — additive subpackage:ketu.declinationis a purely additive companion to the v1.5 declination δ infrastructure. Thebody_declfield (shape(14,)) shipped in v1.5 is the sole input;CHART_DTYPEis byte-identical to v1.5 (no ratchet break). The new names are reachable viaketu.declination.*only —ketu.__all__is unchanged.Parallel ≠ longitude conjunction: declination aspects are independent of ecliptic-longitude aspects. The frozen 14-row
core.aspectstable is byte-identical to v1.5.
[1.5.0] - 2026-06-04
Added 1.5.0
declination(jdate, body)— equatorial declination δ: returns δ in degrees [−90, +90] (north positive, south negative). Scalar and vectorized (arrayjdateviacalc_planet_position_batch, loop-free). Computed via the ecliptic-to-equatorial chain (spherical_to_rectangular → ecliptic_to_equatorial → rectangular_to_spherical), numerically equivalent to Meeus eq. 13.4 to machine precision.declination_velocity(jdate, body): dδ/dt in degrees/day (positive = northward). Forward finite difference, step 0.01 day — mirrors thelat_velocityFD idiom.is_ascending_declination(jdate, body):Truewhen dδ/dt > 0 (Moon montante). Biodynamic montant/descendant helper. Distinct fromis_ascending(β-trajectory) — the two can disagree for the same body on the same date.is_out_of_bounds(jdate, body):Truewhen |δ| > ε(jd). OOB threshold uses the instantaneous true obliquity (not mean obliquity). The Moon can exceed ε during major lunar standstill (~18.6-year nodal cycle; peak ~2024–2025).CHART_DTYPE—body_declfield (additive): newfloat64[14]field holding equatorial declination δ for all 14 bodies.compute_chartandcalculate_compositeboth populate it via the coordinates chain. Body count remains 14; this is an additive dtype change.--harmonics h<N>CLI surface for dynamic harmonics: theaspectscommand accepts--harmonics h7(and anyh2–h64) to detect dynamic harmonic aspects alongside the static set, via thedynamic_specs=engine path.
Changed 1.5.0
H{h}-{k}dynamic-aspect naming promoted to a public API contract: dynamic harmonic rows are namedH{h}-{k}(harmonich, multiplek), pinned by tests and documented as stable.find_aspect_timinggains adyn_coef=parameter: the orb for a dynamic aspect is derived from(orb[b1] + orb[b2]) / 2 × dyn_coef, matching the detection path.
Fixed 1.5.0
Lunar node mean speed corrected (−0.013 → −0.052954 °/day):
core.bodies['speed']for Rahu and Ketu held a value ~4× too slow. The true nodal regression is 360° over ~18.6 years (≈ −0.052991 °/day); the engine already produced −0.052954 °/day for the nodes, so the table is now consistent with the computed motion.calculate_speed_rationow sources its average speeds fromcore.bodies['speed'](single source of truth).calculate_aspects_batchduplicate-pair rows eliminated: with overlapping orbs (e.g. the EXTENDED set) the batch path could emit more than one row for the same(body1, body2)pair on a single date. Bothcalculate_aspects_vectorizedandcalculate_aspects_batchnow share a single detection core, enforcing “exactly one row per pair” (static-first / dynamic-second, first-match-wins) identically.
Notes 1.5.0
is_ascending(β) unchanged: the existing ecliptic-latitude-basedis_ascendingis byte-for-byte identical to v1.4. The newis_ascending_declinationis an independent, parallel helper.Kala impact (additive, not breaking for named access):
CHART_DTYPEgainsbody_declas an additive field. Code using named field access (chart["body_lons"]) is unaffected. Code using positional access or.view()on the raw dtype must adapt. The node-speed fix changescore.bodies['speed'][10]/[11].
[1.4.0] - 2026-06-03
Added 1.4.0
generate_harmonic_aspects(h)— dynamic harmonic generator: builds aspect specs on the fly for ANY integer harmonich(2 ≤ h ≤ 64), using the full-circle 360° convention folded to 0–180° (coef = k/h). Returns a structured array that is a drop-in forcore.aspects; pass it as thedynamic_specs=argument tocalculate_aspects,find_aspects_between_dates, andcalculate_synastry. The frozen 14-rowcore.aspectstable and preset fingerprints are byte-identical (parallel code path).Chiron range expanded to 1900–2100:
ketu/data/chiron_coeffs.npzregenerated (2283 Chebyshev segments,jd_start=2415020.5,jd_end=2488069.5), max error 0.001214° over the new range. Pure-NumPy runtime preserved.
Changed 1.4.0
Chiron orb 0° → 4° (Pluto parity):
core.bodies['orb']for Chiron is now 4°, so Chiron now forms scored aspects.Chiron out-of-range behaviour: input outside 1900–2100 is now silently clamped to the nearest segment boundary (previously raised
ValueError).Documentation recentred on the 180°-division default:
concepts.mdaspect tables now show CLASSICAL (5) and TRADITIONAL (7) only; the full-circle minor harmonics (H5/H9/H10 / EXTENDED) remain available in code but are out of the summary tables.
[1.3.0] - 2026-06-01
Added 1.3.0
Chiron (14th body, body_id=13): Centaur body between Saturn and Uranus. Computed via embedded Chebyshev polynomial coefficients (
.npz), valid range 1950–2050, max error 0.005695° (sub-arcminute). Accessed via standard calculation functions:long(jd, 13),lat(jd, 13), etc.ketu/data/chiron_coeffs.npz: Embedded coefficient file (1142 segments, seg=32 days, degree=10, three quantities: longitude, latitude, distance). Generated offline from pyswisseph — not a runtime dependency.ketu/ephemeris/chiron.py: Pure-NumPy Chebyshev evaluator for Chiron positions.
Breaking Changes 1.3.0
CHART_DTYPEbody axis expanded from 13 to 14 bodies (D-08):body_lons,body_lats,body_speedsarrays are now shape(14,)instead of(13,). The aspect matrices are now(14, 14). Code that hardcodes the body count or uses index 12 as the last body must be updated. Index 13 = Chiron.SYNASTRY_BODY_COUNTchanged from 15 to 16: Reflects the additional Chiron body (14 bodies + ASC + MC).
See Migration Guide for upgrade instructions.
[1.2.0] - 2026-04-XX
Added 1.2.0
ketu.charts— Full natal chart (compute_chart): Returns a singleCHART_DTYPEstructured array combining body positions (13 bodies), house cusps, angles (ASC, MC, ARMC, Vertex), and a 13×13 aspect matrix.is_day_chart(jd, lat, lon)sect helper.ketu.synastry— Inter-chart aspect analysis:calculate_synastry(chart_a, chart_b, aspects, orbs, mode)returnsSYNASTRY_DTYPEstructured array with applying/separating flag and orb limits.ketu.composite— Midpoint composite charts:calculate_composite(chart_a, chart_b, system)returns aCHART_DTYPEwhere each body position is the shortest-arc circular midpoint.circular_midpoint(lon_a, lon_b)utility.ketu.returns— Solar and lunar returns:solar_return(natal_jd, …, target_year)andlunar_return(natal_jd, …, target_jd)both returnCHART_DTYPEfor the return chart. Support relocation viareturn_lat/return_lon.ketu.parts— Arabic Parts / Hermetic Lots: Registry-based system with built-infortune,spirit(sect-aware Fortune/Spirit), andmarriage(fixed formula).calculate_part,calculate_all_parts,register,get_part,PartSpec.Additional house systems: Whole Sign (
whole_sign), Equal (equal), Regiomontanus (regiomontanus) added alongside the existing Placidus, Koch, Porphyry (v1.1).Ephemeris refactor:
ORBITAL_ELEMENTSextracted to_elements.py;apply_perturbationsto_perturbations.py;orbital.pyis now a 70-LOC re-export hub.BODY_STRATEGIES dispatch:
planets.pyuses a strategy dict instead of if-elif chains, making body extension (e.g. Chiron in v1.3) clean.Root
conftest.py: Shared session-scoped fixtures consolidated intests/conftest.py.
[1.1.0] - 2026-03-XX
Added 1.1.0
ketu.aspects— Configurable aspect sets:CLASSICAL(5 aspects),TRADITIONAL(7),EXTENDED(14 — default at v1.1, changed toTRADITIONALin v1.3).AspectSetSpec = Union[str, list, ndarray, None],resolve_aspect_set.calculate_aspects(jdate, l_bodies, aspects=None)now accepts an aspect-set spec.ketu.houses— House system calculations:calculate_houses(jd, lat, lon, system, polar_fallback),house_of(planet_lon, cusps),HOUSES_DTYPE,SYSTEMS,HighLatitudeError,register. Initial systems:placidus,koch,porphyry.HOUSES_DTYPE: Structured array with fieldsjd,lat,lon,system,cusps[12],asc,mc,armc,vertex.
[1.0.0] - 2026-02-12
Breaking Changes 1.0.0
Removed export modules:
ketu.export.chart,ketu.export.icalendar— Ketu is now a pure calculation libraryRemoved pandas dependency:
generate_aspect_timeline()returns NumPy structured array; usepd.DataFrame(timeline)for manual conversionRenamed velocity functions:
vlong()→long_velocity(),vlat()→lat_velocity(),vdist_au()→dist_velocity_au()Public API surface:
ketu.__init__.pyexports only metadata + core constants; functions accessed via submodule imports
Fixed 1.0.0
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)
Added 1.0.0
Type hints everywhere: mypy strict mode compliance
NumPy-style docstrings: Examples section in all public functions
Standardized error messages: All
ValueErrormessages include received value and valid optionsNumerical precision guarantees: ±1e-6° for angular separation (documented)
98% test coverage: 408 tests across all modules
[0.4.0] - 2026-01-14
Added 0.4.0
Complex Engine: New
ketu.complexmodule using \(e^{i\theta}\) representation for cycles.Expanded Aspects: Support for 14 aspects (Harmonics H1-H6, H9, H10).
Ephemeris Cache: Persistent cache for O(1) position lookups.
Vectorized Cycles: Refactored
ketu.cyclesto use complex vector operations.ML Features: Direct generation of Machine Learning features (
cos_phase,sin_phase).
[0.3.0] - 2025-12-XX
Added 0.3.0
Pure NumPy: Removed dependency on
pyswissephbinary.New Modules:
ketu.ephemeris,ketu.cycles.Performance: Significant speedups in time series generation.
[0.2.1] - 2025-10-27
Minor fix
[0.2.0] - 2025-10-27
Added 0.2.0
Full packaging setup for a PyPI release
pyproject.tomlmetadata and dependenciesrequirements.txtfor a minimal installPublic exports in
ketu/__init__.pyExpanded README with usage examples
PyPI, Python versions, and license badges
MANIFEST.into ship data filesGitHub Actions workflow for automated tests
GitHub Actions workflow for PyPI publishing
CI coverage for Python 3.9 through 3.13
ketuCLI entry pointSupport for 13 celestial bodies (added True Node)
English and French documentation
Changed 0.2.0
Fixed and hardened the unit tests
Renamed
timea.pyto_timea.py(private module)Optimised package structure for distribution
Aligned the documentation with the new layout
Technical PyPI deployment 0.2.0
Official support for Python 3.10–3.13
Pytest configuration embedded in
pyproject.tomlCoverage configuration for CI analysis
Package installable via
pip install ketuWorks seamlessly in virtual environments
[0.1.0] - 2024-01-XX
Added 0.1.0
Interactive CLI to calculate positions and aspects
Planetary position calculations via pyswisseph
Detection of major aspects with orbs
Conversions between time systems (UTC, Julian)
Retrogradation detection
Complete documentation with Sphinx and MyST
Installable PyPI package
Initial unit tests
Features 0.1.0
Support for 10 planets + Rahu + Lilith
7 major aspects (conjunction through opposition)
Zodiac sign calculations
Orb system based on Abu Ma’shar
LRU cache to improve performance
Technical 0.1.0
Requires Python 3.9+
Dependencies: numpy, pyswisseph
Modular architecture
Documented, typed code
[0.0.1] - 2023-01-XX
Initial
Prototype
Basic position calculations
Command-line interface
Versioning Convention
MAJOR: Incompatible API changes
MINOR: Backward-compatible feature additions
PATCH: Backward-compatible bug fixes