FAILURE MAP
← Case archive

FA-69346 / Orbital propagation / Open access

Elliptic Kepler solver: Mean anomaly is not reduced to one revolution · case 01

Multi-revolution or negative mean anomalies return eccentric anomalies outside [0,2pi).

Verified by executionVariant 1 · 7 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

The mean anomaly is used without reduction modulo 2pi.

VERIFIED REPAIR

Reduce M into [0,2pi) before iterating.

Unsuccessful approach: Reducing modulo 360 treats a radian angle as degrees.

Case contract

Input [M, e] (radians). Return None unless 0<=e<1. Reduce M into [0,2pi), start Newton at M (e<0.8) or pi, iterate E -= (E-e sinE-M)/(1-e cosE) up to 50 times until the step is below 1e-13, and return E rounded to 9 decimals.

Why this case matters

Orbit determination and mission planning chain many small conversions; one wrong branch or unit silently moves a spacecraft by kilometres.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
    M,e=x
    if e<0 or e>=1: return None
    pass
    E=M if e<0.8 else math.pi
    for _ in range(50):
        d=(E-e*math.sin(E)-M)/(1-e*math.cos(E))
        E-=d
        if abs(d)<1e-13: break
    return round(E,9)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('elliptic kepler solver [-1.0, 0.2]', [-1.0, 0.2], 5.097861103), ('elliptic kepler solver [7.5, 0.45]', [7.5, 0.45], 1.66482677), ('elliptic kepler solver [0.5, 0.1]', [0.5, 0.1], 0.552479987), ('elliptic kepler solver [2.0, 0.3]', [2.0, 0.3], 2.236031495), ('elliptic kepler solver [5.5, 0.6]', [5.5, 0.6], 4.911902084), ('elliptic kepler solver [0.05, 0.97]', [0.05, 0.97], 0.588295814), ('elliptic kepler solver [3.0, 0.99]', [3.0, 0.99], 3.070410669)], [('elliptic kepler solver [7.5, 0.45]', [7.5, 0.45], 1.66482677), ('elliptic kepler solver [20.0, 0.05]', [20.0, 0.05], 1.196991311), ('elliptic kepler solver [0.05, 0.97]', [0.05, 0.97], 0.588295814), ('elliptic kepler solver [3.0, 0.99]', [3.0, 0.99], 3.070410669), ('elliptic kepler solver [-1.0, 0.2]', [-1.0, 0.2], 5.097861103), ('elliptic kepler solver [0.0, 0.7]', [0.0, 0.7], 0.0), ('elliptic kepler solver [3.141592653589793, 0.5]', [3.141592653589793, 0.5], 3.141592654)], [('elliptic kepler solver [20.0, 0.05]', [20.0, 0.05], 1.196991311), ('elliptic kepler solver [-8.0, 0.75]', [-8.0, 0.75], 3.999173912), ('elliptic kepler solver [7.5, 0.45]', [7.5, 0.45], 1.66482677), ('elliptic kepler solver [0.0, 0.7]', [0.0, 0.7], 0.0), ('elliptic kepler solver [3.141592653589793, 0.5]', [3.141592653589793, 0.5], 3.141592654), ('elliptic kepler solver [1.0, 1.0]', [1.0, 1.0], None), ('elliptic kepler solver [1.0, -0.1]', [1.0, -0.1], None)], [('elliptic kepler solver [-8.0, 0.75]', [-8.0, 0.75], 3.999173912), ('elliptic kepler solver [7.5, 0.45]', [7.5, 0.45], 1.66482677), ('elliptic kepler solver [3.141592653589793, 0.5]', [3.141592653589793, 0.5], 3.141592654), ('elliptic kepler solver [1.0, 1.0]', [1.0, 1.0], None), ('elliptic kepler solver [1.0, -0.1]', [1.0, -0.1], None), ('elliptic kepler solver [0.2, 0.85]', [0.2, 0.85], 0.823499414), ('elliptic kepler solver [6.0, 0.93]', [6.0, 0.93], 5.162716882)], [('elliptic kepler solver [-1.0, 0.2]', [-1.0, 0.2], 5.097861103), ('elliptic kepler solver [20.0, 0.05]', [20.0, 0.05], 1.196991311), ('elliptic kepler solver [0.2, 0.85]', [0.2, 0.85], 0.823499414), ('elliptic kepler solver [6.0, 0.93]', [6.0, 0.93], 5.162716882), ('elliptic kepler solver [-8.0, 0.75]', [-8.0, 0.75], 3.999173912), ('elliptic kepler solver [1.3, 0.0]', [1.3, 0.0], 1.3), ('elliptic kepler solver [0.5, 0.1]', [0.5, 0.1], 0.552479987)]]
for label, args, expected in fixtures[N-1]:
    check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
elliptic kepler solver [-1.0, 0.2]-1.1853242045.097861103Failed
elliptic kepler solver [7.5, 0.45]7.9480120771.66482677Failed
elliptic kepler solver [0.5, 0.1]0.5524799870.552479987Passed
elliptic kepler solver [2.0, 0.3]2.2360314952.236031495Passed
elliptic kepler solver [5.5, 0.6]4.9119020844.911902084Passed
elliptic kepler solver [0.05, 0.97]0.5882958140.588295814Passed
elliptic kepler solver [3.0, 0.99]3.0704106693.070410669Passed

SHA-256 / 4cc679f9fe972e4be8b714fc042afca378ac10bf0c5f16e290052f0ba76c1034

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
    M,e=x
    if e<0 or e>=1: return None
    M=math.fmod(M,360)
    if M<0: M+=2*math.pi
    E=M if e<0.8 else math.pi
    for _ in range(50):
        d=(E-e*math.sin(E)-M)/(1-e*math.cos(E))
        E-=d
        if abs(d)<1e-13: break
    return round(E,9)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('elliptic kepler solver [-1.0, 0.2]', [-1.0, 0.2], 5.097861103), ('elliptic kepler solver [7.5, 0.45]', [7.5, 0.45], 1.66482677), ('elliptic kepler solver [0.5, 0.1]', [0.5, 0.1], 0.552479987), ('elliptic kepler solver [2.0, 0.3]', [2.0, 0.3], 2.236031495), ('elliptic kepler solver [5.5, 0.6]', [5.5, 0.6], 4.911902084), ('elliptic kepler solver [0.05, 0.97]', [0.05, 0.97], 0.588295814), ('elliptic kepler solver [3.0, 0.99]', [3.0, 0.99], 3.070410669)], [('elliptic kepler solver [7.5, 0.45]', [7.5, 0.45], 1.66482677), ('elliptic kepler solver [20.0, 0.05]', [20.0, 0.05], 1.196991311), ('elliptic kepler solver [0.05, 0.97]', [0.05, 0.97], 0.588295814), ('elliptic kepler solver [3.0, 0.99]', [3.0, 0.99], 3.070410669), ('elliptic kepler solver [-1.0, 0.2]', [-1.0, 0.2], 5.097861103), ('elliptic kepler solver [0.0, 0.7]', [0.0, 0.7], 0.0), ('elliptic kepler solver [3.141592653589793, 0.5]', [3.141592653589793, 0.5], 3.141592654)], [('elliptic kepler solver [20.0, 0.05]', [20.0, 0.05], 1.196991311), ('elliptic kepler solver [-8.0, 0.75]', [-8.0, 0.75], 3.999173912), ('elliptic kepler solver [7.5, 0.45]', [7.5, 0.45], 1.66482677), ('elliptic kepler solver [0.0, 0.7]', [0.0, 0.7], 0.0), ('elliptic kepler solver [3.141592653589793, 0.5]', [3.141592653589793, 0.5], 3.141592654), ('elliptic kepler solver [1.0, 1.0]', [1.0, 1.0], None), ('elliptic kepler solver [1.0, -0.1]', [1.0, -0.1], None)], [('elliptic kepler solver [-8.0, 0.75]', [-8.0, 0.75], 3.999173912), ('elliptic kepler solver [7.5, 0.45]', [7.5, 0.45], 1.66482677), ('elliptic kepler solver [3.141592653589793, 0.5]', [3.141592653589793, 0.5], 3.141592654), ('elliptic kepler solver [1.0, 1.0]', [1.0, 1.0], None), ('elliptic kepler solver [1.0, -0.1]', [1.0, -0.1], None), ('elliptic kepler solver [0.2, 0.85]', [0.2, 0.85], 0.823499414), ('elliptic kepler solver [6.0, 0.93]', [6.0, 0.93], 5.162716882)], [('elliptic kepler solver [-1.0, 0.2]', [-1.0, 0.2], 5.097861103), ('elliptic kepler solver [20.0, 0.05]', [20.0, 0.05], 1.196991311), ('elliptic kepler solver [0.2, 0.85]', [0.2, 0.85], 0.823499414), ('elliptic kepler solver [6.0, 0.93]', [6.0, 0.93], 5.162716882), ('elliptic kepler solver [-8.0, 0.75]', [-8.0, 0.75], 3.999173912), ('elliptic kepler solver [1.3, 0.0]', [1.3, 0.0], 1.3), ('elliptic kepler solver [0.5, 0.1]', [0.5, 0.1], 0.552479987)]]
for label, args, expected in fixtures[N-1]:
    check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
elliptic kepler solver [-1.0, 0.2]5.0978611035.097861103Passed
elliptic kepler solver [7.5, 0.45]7.9480120771.66482677Failed
elliptic kepler solver [0.5, 0.1]0.5524799870.552479987Passed
elliptic kepler solver [2.0, 0.3]2.2360314952.236031495Passed
elliptic kepler solver [5.5, 0.6]4.9119020844.911902084Passed
elliptic kepler solver [0.05, 0.97]0.5882958140.588295814Passed
elliptic kepler solver [3.0, 0.99]3.0704106693.070410669Passed

SHA-256 / c029f46b5fcc14082c9bcb3fb45139c69b033418011a296d4e7954c7c83a3d0c

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
    M,e=x
    if e<0 or e>=1: return None
    M=math.fmod(M,2*math.pi)
    if M<0: M+=2*math.pi
    E=M if e<0.8 else math.pi
    for _ in range(50):
        d=(E-e*math.sin(E)-M)/(1-e*math.cos(E))
        E-=d
        if abs(d)<1e-13: break
    return round(E,9)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('elliptic kepler solver [-1.0, 0.2]', [-1.0, 0.2], 5.097861103), ('elliptic kepler solver [7.5, 0.45]', [7.5, 0.45], 1.66482677), ('elliptic kepler solver [0.5, 0.1]', [0.5, 0.1], 0.552479987), ('elliptic kepler solver [2.0, 0.3]', [2.0, 0.3], 2.236031495), ('elliptic kepler solver [5.5, 0.6]', [5.5, 0.6], 4.911902084), ('elliptic kepler solver [0.05, 0.97]', [0.05, 0.97], 0.588295814), ('elliptic kepler solver [3.0, 0.99]', [3.0, 0.99], 3.070410669)], [('elliptic kepler solver [7.5, 0.45]', [7.5, 0.45], 1.66482677), ('elliptic kepler solver [20.0, 0.05]', [20.0, 0.05], 1.196991311), ('elliptic kepler solver [0.05, 0.97]', [0.05, 0.97], 0.588295814), ('elliptic kepler solver [3.0, 0.99]', [3.0, 0.99], 3.070410669), ('elliptic kepler solver [-1.0, 0.2]', [-1.0, 0.2], 5.097861103), ('elliptic kepler solver [0.0, 0.7]', [0.0, 0.7], 0.0), ('elliptic kepler solver [3.141592653589793, 0.5]', [3.141592653589793, 0.5], 3.141592654)], [('elliptic kepler solver [20.0, 0.05]', [20.0, 0.05], 1.196991311), ('elliptic kepler solver [-8.0, 0.75]', [-8.0, 0.75], 3.999173912), ('elliptic kepler solver [7.5, 0.45]', [7.5, 0.45], 1.66482677), ('elliptic kepler solver [0.0, 0.7]', [0.0, 0.7], 0.0), ('elliptic kepler solver [3.141592653589793, 0.5]', [3.141592653589793, 0.5], 3.141592654), ('elliptic kepler solver [1.0, 1.0]', [1.0, 1.0], None), ('elliptic kepler solver [1.0, -0.1]', [1.0, -0.1], None)], [('elliptic kepler solver [-8.0, 0.75]', [-8.0, 0.75], 3.999173912), ('elliptic kepler solver [7.5, 0.45]', [7.5, 0.45], 1.66482677), ('elliptic kepler solver [3.141592653589793, 0.5]', [3.141592653589793, 0.5], 3.141592654), ('elliptic kepler solver [1.0, 1.0]', [1.0, 1.0], None), ('elliptic kepler solver [1.0, -0.1]', [1.0, -0.1], None), ('elliptic kepler solver [0.2, 0.85]', [0.2, 0.85], 0.823499414), ('elliptic kepler solver [6.0, 0.93]', [6.0, 0.93], 5.162716882)], [('elliptic kepler solver [-1.0, 0.2]', [-1.0, 0.2], 5.097861103), ('elliptic kepler solver [20.0, 0.05]', [20.0, 0.05], 1.196991311), ('elliptic kepler solver [0.2, 0.85]', [0.2, 0.85], 0.823499414), ('elliptic kepler solver [6.0, 0.93]', [6.0, 0.93], 5.162716882), ('elliptic kepler solver [-8.0, 0.75]', [-8.0, 0.75], 3.999173912), ('elliptic kepler solver [1.3, 0.0]', [1.3, 0.0], 1.3), ('elliptic kepler solver [0.5, 0.1]', [0.5, 0.1], 0.552479987)]]
for label, args, expected in fixtures[N-1]:
    check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
elliptic kepler solver [-1.0, 0.2]5.0978611035.097861103Passed
elliptic kepler solver [7.5, 0.45]1.664826771.66482677Passed
elliptic kepler solver [0.5, 0.1]0.5524799870.552479987Passed
elliptic kepler solver [2.0, 0.3]2.2360314952.236031495Passed
elliptic kepler solver [5.5, 0.6]4.9119020844.911902084Passed
elliptic kepler solver [0.05, 0.97]0.5882958140.588295814Passed
elliptic kepler solver [3.0, 0.99]3.0704106693.070410669Passed

SHA-256 / fe85f6ccfa5b4e1d15cdb4fa69af72c42005dc92032f0a957678fbfbfb79fb47

Verification & scope

A deterministic toy two-body model with stipulated constants and conventions; not flight dynamics software or a validated SGP4 implementation. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.

Observations recorded using Python 3.12.14 at 2026-09-29T14:48:10.618463+00:00.

Case digest / f42cd844e057377e964f91e1dab6bf9e6cd778fc0c0b386500f96f7ec9b021ce