Skip to content

Optimize fid lights for guide stars#410

Merged
jeanconn merged 78 commits intomasterfrom
guide-opt-fid
Jan 14, 2026
Merged

Optimize fid lights for guide stars#410
jeanconn merged 78 commits intomasterfrom
guide-opt-fid

Conversation

@jeanconn
Copy link
Contributor

@jeanconn jeanconn commented Nov 12, 2025

Joint Optimization of Guide Stars and Fid Lights

This PR implements joint optimization of guide star and fid light selection to find the best compromise when fids and guides conflict. Previously, guide star selection always avoided selected fid lights, but fid selection was unaware of its impact on guide candidates. This could lead to suboptimal catalogs where fid lights spoiled potential guide stars, reducing guide count below acceptable levels. The new optimization evaluates multiple fid configurations and selects the one that best balances acquisition probability with guide star availability.

In most observations, the initial fid selection (via the first call to get_fid_catalog in _get_aca_catalog) trivially selects a fid set that is non-impacting to guide candidates and acquisition stars and in which the fid lights themselves are "unspoiled" by field stars. If so, no further optimization is needed.

Fixes #300

Reviewed and approved by SSAWG 14-Jan-2026

Architecture Changes

Candidate Reuse Pattern: The optimization now generates initial guide candidates once via the new get_guide_candidates() function and reuses them across multiple iterations when evaluating different fid configurations. This avoids redundant candidate filtering and improves performance during optimization loops that typically run 3-10 iterations.

Guide Count Metric: In addition to acquisition probability (P2), optimization now tracks guide_count. The optimization prefers fid sets meeting minimum thresholds (P2 ≥ 2.0 AND guide_count ≥ 4.0) over sets that only maximize P2. When no "passable" sets exist, it falls back to maximizing P2.

Monitor Window Handling: Monitor keep-out zones are now enforced via direct candidate filtering (filter_candidates_for_monitors()). This is more straightforward than the previous handling via modifying the dark map.

Dynamic Background Function: The get_t_ccds_bonus() function has been relocated from the sparkles package into proseco to support guide count calculations during optimization. This avoids requiring sparkles as a dependency to run proseco.

Interface Changes

New public API:

  • get_guide_candidates(**kwargs): Returns initial guide candidate table without fid filtering or final selection
  • get_t_ccds_bonus(mags, t_ccd, dyn_bgd_n_faint, dyn_bgd_dt_ccd): Relocated from sparkles for computing dynamic background temperature bonuses

Modified signatures:

  • get_guide_catalog(..., initial_guide_cands=None, ...): Accepts pre-computed candidates for reuse
  • get_fid_catalog(..., guide_cands=None, ...): Accepts guide candidates to evaluate spoiling scenarios
  • optimize_acqs_fids(initial_guide_cands=None, **kwargs): Passes candidates through optimization loop

All changes maintain backward compatibility - new parameters are optional and default to original behavior, so these are not interface impacts.

Test Coverage

New test file test_guide_fid_optimization.py provides comprehensive coverage:

  • Fid trap detection and optimization scenarios (6 test cases)
  • Guide-fid trade-offs with various spoiling configurations
  • Candidate reuse with fids and monitors
  • Column initialization for include_ids stars

Extended tests in test_guide.py:

  • Dynamic background bonus calculations (test_get_t_ccds_bonus_* - 4 tests)
  • Monitor keep-out zone filtering (test_filter_candidates_for_monitors)
  • Initial candidate reuse patterns (3 tests)

Tests verify that optimization runs when fid spoiling occurs and selects appropriate fid sets. The tests confirm that guide stars avoid selected fids and that fid trap spoilers are correctly excluded.

Implementation Notes

The optimization logic (lines 582-640 in catalog.py) explicitly checks for "passable" sets before selecting the best configuration. This operational knowledge was previously implicit in downstream sparkles review but is now codified for better catalog quality during optimization.

Column initialization for candidates happens after filtering operations to avoid unnecessary table copies and prevent in-place modifications of shared data structures.

Interface impacts

Testing

Unit tests

  • Mac
^[[A(ska3-flight-2026.1rc1) flame:proseco jean$ pytest
==================================================================================== test session starts =====================================================================================
platform darwin -- Python 3.13.10, pytest-9.0.1, pluggy-1.6.0
rootdir: /Users/jean/git
configfile: pytest.ini
plugins: anyio-4.12.0, timeout-2.4.0
collected 250 items                                                                                                                                                                          

proseco/tests/test_acq.py .....................................                                                                                                                        [ 14%]
proseco/tests/test_catalog.py ..........................................                                                                                                               [ 31%]
proseco/tests/test_core.py ............................                                                                                                                                [ 42%]
proseco/tests/test_diff.py ......                                                                                                                                                      [ 45%]
proseco/tests/test_fid.py ...............                                                                                                                                              [ 51%]
proseco/tests/test_guide.py ............................................................                                                                                               [ 75%]
proseco/tests/test_guide_fid_optimization.py ............                                                                                                                              [ 80%]
proseco/tests/test_jupiter.py ............................................                                                                                                             [ 97%]
proseco/tests/test_mon_full_cat.py ......                                                                                                                                              [100%]

=============================================================================== 250 passed in 73.26s (0:01:13) ===============================================================================
(ska3-flight-2026.1rc1) flame:proseco jean$ git rev-parse HEAD
5bcd1ea2e8dbeef780aef2797bb9116be9a741f2

Independent check of unit tests by [REVIEWER NAME]

  • [PLATFORM]:

Functional tests

No functional testing.

@jeanconn
Copy link
Contributor Author

jeanconn commented Dec 4, 2025

With the changes so far in this PR it overall isn't that much slower using this kind of test

Switched to branch 'master'
Your branch is up to date with 'origin/master'.
(latest) flame:proseco jean$ ipython
Python 3.12.8 | packaged by conda-forge | (main, Dec  5 2024, 14:19:53) [Clang 18.1.8 ]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.31.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: from proseco.tests.timing import time_get_aca_catalog

In [2]: time_get_aca_catalog()
....................................................................................................
Got 100 catalogs in 0.126 secs (mean)

In [3]: exit
(latest) flame:proseco jean$ git checkout guide-opt-fid
Switched to branch 'guide-opt-fid'
(latest) flame:proseco jean$ ipython
Python 3.12.8 | packaged by conda-forge | (main, Dec  5 2024, 14:19:53) [Clang 18.1.8 ]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.31.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: from proseco.tests.timing import time_get_aca_catalog

In [2]: time_get_aca_catalog()
....................................................................................................
Got 100 catalogs in 0.130 secs (mean)

@jeanconn
Copy link
Contributor Author

You do have me thinking @taldcroft that I might be able to simplify this further if I call get_monitor_catalog earlier in get_aca_catalog() and then figure out a way to deal with the possible monitor include_ids and changes to n_guide better. I'll ponder it.

@taldcroft
Copy link
Member

@jeanconn - the only thing I'm really waiting for at this point is the comparison of P2 and guide_count for the 10% of catalogs that changed.

@jeanconn
Copy link
Contributor Author

The pr410 notebook now has some plots for that - they seem reasonable to me. Should I also just put the plots in the PR description on github?

Copy link
Member

@taldcroft taldcroft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good enough! You can always come back and fine tune the code if needed.

@jeanconn jeanconn merged commit 2debc11 into master Jan 14, 2026
2 checks passed
@jeanconn jeanconn deleted the guide-opt-fid branch January 14, 2026 18:06
This was referenced Jan 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fid selection should consider guide star selection

3 participants