Smithsonian / Smithsonian/layup
IOD is confined to seq[0], which is too thin on sparse multi-night data
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 10
- Forks
- 2
- Avg merge
- 1d 43m
- Merged PRs (30d)
- 58
Description
do_fit runs IOD on the primary segment alone (orbitfit.py:1181):
obs = [observations[i] for i in seq[0]]
_build_sequence splits on 90-day gaps, so when a survey reports one observation per night and the nights are months apart, every segment is one night. Both IOD paths then close:
- Gauss.
_select_gauss_tripletreturnsNonebelow three observations, and the fallback (iod.py:15) builds(0, 1, 1)from a two-element segment -- a repeated index, unguarded. The C++ binding returnsNonefor it, so nothing worse happens, but the fallback is constructing an input it knows is invalid and relying on the callee to reject it. - BK-IOD. The fallback is gated on
len(obs) >= 3against that same segment (orbitfit.py:1206), so it never runs.
With no candidates, do_fit returns flag = 5, stage = 1.
Measured
The 110 trans-Neptunian objects of Smotherman et al. 2024 (AJ 167, 136), from a DEEP shift-and-stack search, detected on four or five nights over two years, fitted through the streak path (iod="auto", engine="bk_native", one streak observation per night):
len(seq[0]) |
objects | outcome |
|---|---|---|
| 2 | 102 | flag 5, every one |
| 3 | 8 | 1 flag 0, 6 flag 4, 1 exceeded a 120 s timeout |
The separation is exact: every object whose primary segment holds two observations fails with no solution, and no object whose segment holds three does. In point form -- the same detections, two per night -- len(seq[0]) is 4 or 6 and never below 3.
run_bk_iod on the full observation set converges for 110 of 110, on genuine streak observations as well as on the point form. So the seed the fallback would need is available and good; the len(obs) >= 3 gate against seq[0] is what prevents it being asked for.
One object, D458636, exceeds 120 s under per-object isolation against roughly five seconds for the rest. Separately, a single long-lived process fitting these in sequence stalled at 100 per cent CPU for 40 minutes on one object before being killed; per-object forks did not reproduce it on that object, so it may be accumulated state rather than the object.
Suggested change
- When the primary segment cannot support an IOD, widen the input rather than declining: run BK-IOD on the full observation set before returning
FLAG_NO_SOLUTION. The measurement above says the seed is there for all 110. - Guard the Gauss fallback: if
_select_gauss_tripletreturnsNoneandlen(idx0) < 3, return no candidates instead of building a triplet with a repeated index. Testable without any fitting.
Related
- #556 -- same
seq[0], the screen rather than the IOD input. - #586 -- BK-IOD reachable only as a fallback after every Gauss root fails, never by request.
Drafted with Claude Opus 5.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in orbitfit.py at do_fit around lines 1181 and 1206, then inspect the Gauss fallback in iod.py around line 15. Reproduce a sparse multi-night case and trace the primary segment versus the full observation set. Done means the full set can reach BK-IOD when the segment is insufficient, and invalid short Gauss inputs produce no candidates; add regression coverage for both paths.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp, python
- Domain
- backend, data
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100