fix(solar-return): return instant in the solver's frame, annual chart keeps seconds (BUG-1220)
The natal Sun target came from compute_chart_data while the solver iterates on _get_sun_lon_jd (swe sidereal flags); the frames differ by up to ~17", moving the return by up to ~7 minutes. The annual chart was also cast at the truncated minute. Ports upstream yinduzhanxing 7cc6425d / 46ed9399 (these two hunks only). PyJHora reference: 3 cases within 0.03 min. 243 public-figure annual charts: annual Lagna changes 13, year lord 11, Mudda first lord 0. v5 77 cases identical to 3c27c38e; consult goldens re-captured byte-identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
d0b783bd05
commit
01929baea6
@@ -0,0 +1,81 @@
|
||||
"""Solar-return instant in the solver's frame, annual chart with seconds (BUG-1220).
|
||||
|
||||
The return is the instant the Sun regains its natal sidereal longitude. The
|
||||
website took the natal Sun from ``compute_chart_data`` while
|
||||
``_find_solar_return_swe`` iterates on ``_get_sun_lon_jd`` (swe sidereal
|
||||
flags); the two differ by up to ~17", which moved the return by up to ~7
|
||||
minutes and changed the annual Ascendant sign in 13 of 243 public-figure
|
||||
years. The annual chart was also cast at the truncated minute. Upstream
|
||||
yinduzhanxing 7cc6425d / 46ed9399.
|
||||
|
||||
Reference instants were computed once with PyJHora ``drik.next_solar_date``
|
||||
(Lahiri) as an external reference engine; only the resulting numbers are kept
|
||||
here, PyJHora is not imported. Charts: Sanjay Rath's "standard nativity" and
|
||||
two public figures from references/real_case_calibration.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import io
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SCRIPTS = ROOT / "scripts"
|
||||
if str(SCRIPTS) not in sys.path:
|
||||
sys.path.insert(0, str(SCRIPTS))
|
||||
|
||||
import solar_return # noqa: E402
|
||||
|
||||
try: # pragma: no cover - environment probe
|
||||
import swisseph # noqa: F401
|
||||
|
||||
HAS_SWE = True
|
||||
except Exception: # pragma: no cover
|
||||
HAS_SWE = False
|
||||
|
||||
pytestmark = pytest.mark.skipif(not HAS_SWE, reason="swisseph required")
|
||||
|
||||
# (birth y, m, d, h, min, lat, lon, tz), target year, PyJHora return JD (UT)
|
||||
PYJHORA_RETURNS = {
|
||||
"rath_standard_nativity_2025": ((1963, 8, 7, 21, 15, 21.4667, 84.0167, 5.5), 2025, 2460895.0424092),
|
||||
"steve_jobs_2012": ((1955, 2, 24, 19, 15, 37.7749, -122.4194, -8.0), 2012, 2455983.2569387),
|
||||
"zinedine_zidane_2002": ((1972, 6, 23, 3, 0, 43.3, 5.4, 1.0), 2002, 2452449.2759534),
|
||||
}
|
||||
|
||||
|
||||
def _chart(birth, target_year):
|
||||
with contextlib.redirect_stdout(io.StringIO()):
|
||||
return solar_return.calc_solar_return_chart(*birth, target_year, "lahiri")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("case", sorted(PYJHORA_RETURNS))
|
||||
def test_return_instant_matches_the_external_reference(case: str) -> None:
|
||||
birth, target_year, reference_jd = PYJHORA_RETURNS[case]
|
||||
result = _chart(birth, target_year)
|
||||
assert result["solar_return"]["target_sun_frame"] == "swe_sidereal_flags"
|
||||
# Before BUG-1220 these were 6-7 minutes apart.
|
||||
assert abs(result["solar_return"]["jd_ut"] - reference_jd) * 1440 <= 0.5
|
||||
|
||||
|
||||
def test_target_longitude_is_the_natal_sun_in_the_solver_frame() -> None:
|
||||
birth, target_year, _ = PYJHORA_RETURNS["rath_standard_nativity_2025"]
|
||||
with contextlib.redirect_stdout(io.StringIO()):
|
||||
from jyotish_engine import compute_chart_data
|
||||
natal, _asc, natal_jd, _ay = compute_chart_data(*birth, "mean", ayanamsa_name="lahiri")
|
||||
solver_sun = solar_return._get_sun_lon_jd(natal_jd, ayanamsa_name="lahiri")
|
||||
chart_sun = natal["planets"]["Sun"]["degree_raw"]
|
||||
assert abs(chart_sun - solver_sun) * 3600 > 10 # the two frames really differ here
|
||||
result = _chart(birth, target_year)
|
||||
assert abs(result["solar_return"]["sun_lon"] - solver_sun) * 3600 < 1
|
||||
|
||||
|
||||
def test_annual_chart_keeps_the_seconds_of_the_return() -> None:
|
||||
birth, target_year, _ = PYJHORA_RETURNS["rath_standard_nativity_2025"]
|
||||
result = _chart(birth, target_year)
|
||||
info = result["chart"]["birth_info"]
|
||||
return_dt = result["solar_return"]["dt_ut"]
|
||||
assert (info["hour"], info["minute"], info["second"]) == (return_dt.hour, return_dt.minute, return_dt.second)
|
||||
assert abs(info["julian_day"] - result["solar_return"]["jd_ut"]) * 86400 < 1
|
||||
Reference in New Issue
Block a user