Skip to content

Commit

Permalink
Merge pull request #174 from sot/include-exclude
Browse files Browse the repository at this point in the history
Add API and attr support for include/exclude stars for guide
  • Loading branch information
taldcroft authored Nov 29, 2018
2 parents eb9ec2e + 3efc495 commit 1c14d0e
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 52 deletions.
18 changes: 10 additions & 8 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,16 @@ focus_offset SIM focus offset [steps] (default=0)

**Optional**

============== =========================================================
Argument Description
============== =========================================================
include_ids list of AGASC IDs of stars to include in selected catalog
include_halfws list of acq halfwidths corresponding to ``include_ids``
exclude_ids list of AGASC IDs of stars to exclude from selected catalog
stars table of AGASC stars (will be fetched from agasc if None)
============== =========================================================
=================== =========================================================
Argument Description
=================== =========================================================
include_ids_acq list of AGASC IDs of stars to include in acq catalog
include_halfws_acq list of acq halfwidths corresponding to ``include_ids``
exclude_ids_acq list of AGASC IDs of stars to exclude from acq catalog
include_ids_guide list of AGASC IDs of stars to include in guide catalog
exclude_ids_guide list of AGASC IDs of stars to exclude from guide catalog
stars table of AGASC stars (will be fetched from agasc if None)
=================== =========================================================

**Debug**

Expand Down
22 changes: 6 additions & 16 deletions proseco/acq.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from . import characteristics as CHAR
from .core import (get_mag_std, ACACatalogTable, bin2x2,
get_image_props, pea_reject_image, ACABox,
MetaAttribute, calc_spoiler_impact)
MetaAttribute, AliasAttribute, calc_spoiler_impact)


def get_acq_catalog(obsid=0, **kwargs):
Expand Down Expand Up @@ -138,21 +138,11 @@ def empty(cls):
out['halfw'] = np.full(fill_value=0, shape=(0,), dtype=np.int64)
return out

@property
def t_ccd(self):
return self.t_ccd_acq

@t_ccd.setter
def t_ccd(self, value):
self.t_ccd_acq = value

@property
def dither(self):
return self.dither_acq

@dither.setter
def dither(self, value):
self.dither_acq = value
t_ccd = AliasAttribute()
dither = AliasAttribute()
include_ids = AliasAttribute()
include_halfws = AliasAttribute()
exclude_ids = AliasAttribute()

@property
def fid_set(self):
Expand Down
8 changes: 5 additions & 3 deletions proseco/catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ def get_aca_catalog(obsid=0, **kwargs):
:param sim_offset: SIM translation offset from nominal [steps] (default=0)
:param focus_offset: SIM focus offset [steps] (default=0)
:param stars: table of AGASC stars (will be fetched from agasc if None)
:param include_ids: list of AGASC IDs of stars to include in selected catalog
:param include_halfws: list of acq halfwidths corresponding to ``include_ids``
:param exclude_ids: list of AGASC IDs of stars to exclude from selected catalog
:param include_ids_acq: list of AGASC IDs of stars to include in acq catalog
:param include_halfws_acq: list of acq halfwidths corresponding to ``include_ids``
:param exclude_ids_acq: list of AGASC IDs of stars to exclude from acq catalog
:param include_ids_guide: list of AGASC IDs of stars to include in guide catalog
:param exclude_ids_guide: list of AGASC IDs of stars to exclude from guide catalog
:param optimize: optimize star catalog after initial selection (default=True)
:param verbose: provide extra logging info (mostly calc_p_safe) (default=False)
:param print_log: print the run log to stdout (default=False)
Expand Down
50 changes: 45 additions & 5 deletions proseco/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,44 @@ def __repr__(self):
return f'<ACABox y={self.y} z={self.z}>'


class AliasAttribute:
"""Descriptor to define class attributes which can be accessed via
either <name> or <name>_<subclass>.
For instance the ``dither`` attribute is found in both AcqTable and
GuideTable, but in general these can be different for the two cases.
The ``get_aca_catalog()`` function handles this by accepting ``dither_acq``
and ``dither_guide``. In order to be able to generically pass the
same commmon kwargs to ``get_acq_catalog`` and ``get_guide_catalog``,
we define the AliasAttribute to allow set/get of either ``dither``
or ``dither_acq`` (or ``dither_guide``).
The <subclass> name is the lower case version of everything before
``Table`` in the subclass name, so GuideTable => 'guide'.
"""
def __get__(self, instance, owner):
if instance is None:
# When called without an instance, return self to allow access
# to descriptor attributes.
return self
else:
return getattr(instance, self.alias)

def __set__(self, instance, value):
setattr(instance, self.alias, value)

def __set_name__(self, owner, name):
if owner.__name__.endswith('Table'):
self.alias = name + '_' + owner.__name__[:-5].lower()
owner.allowed_kwargs.add(name)
else:
raise ValueError('can only be used in classes named *Table')

def __repr__(self):
return (f'<{self.__class__.__name__} alias={self.alias}')


class MetaAttribute:
def __init__(self, default=None, is_kwarg=True, pickle=True):
"""
Expand Down Expand Up @@ -290,8 +328,8 @@ class ACACatalogTable(Table):
# Should be set by subclass, e.g. ``name = 'acqs'`` for AcqTable.
name = 'aca_cat'

# Catalog attributes, gets set in MetaAttribute
allowed_kwargs = set(['dither', 't_ccd'])
# Catalog attributes, gets set in MetaAttribute or AliasAttribute
allowed_kwargs = set()

required_attrs = ('dither_acq', 'dither_guide', 'date')

Expand All @@ -311,9 +349,11 @@ class ACACatalogTable(Table):
focus_offset = MetaAttribute()
dark = MetaAttribute(pickle=False)
stars = MetaAttribute(pickle=False)
include_ids = MetaAttribute(default=[])
include_halfws = MetaAttribute(default=[])
exclude_ids = MetaAttribute(default=[])
include_ids_acq = MetaAttribute(default=[])
include_halfws_acq = MetaAttribute(default=[])
exclude_ids_acq = MetaAttribute(default=[])
include_ids_guide = MetaAttribute(default=[])
exclude_ids_guide = MetaAttribute(default=[])
optimize = MetaAttribute(default=True)
verbose = MetaAttribute(default=False)
print_log = MetaAttribute(default=False)
Expand Down
26 changes: 8 additions & 18 deletions proseco/guide.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from . import characteristics as CHAR
from . import characteristics_guide as GUIDE_CHAR

from .core import bin2x2, ACACatalogTable, ACABox, MetaAttribute
from .core import bin2x2, ACACatalogTable, MetaAttribute, AliasAttribute

CCD = GUIDE_CHAR.CCD

Expand All @@ -32,6 +32,8 @@ def get_guide_catalog(obsid=0, **kwargs):
:param n_guide: number of guide stars to attempt to get
:param fids: selected fids (used for guide star exclusion)
:param stars: astropy.Table of AGASC stars (will be fetched from agasc if None)
:param include_ids: list of AGASC IDs of stars to include in guide catalog
:param exclude_ids: list of AGASC IDs of stars to exclude from guide catalog
:param dark: ACAImage of dark map (fetched based on time and t_ccd if None)
:param print_log: print the run log to stdout (default=False)
Expand Down Expand Up @@ -86,21 +88,10 @@ def reject(self, reject):
reject_info = self.reject_info
reject_info.append(reject)

@property
def t_ccd(self):
return self.t_ccd_guide

@t_ccd.setter
def t_ccd(self, value):
self.t_ccd_guide = value

@property
def dither(self):
return self.dither_guide

@dither.setter
def dither(self, value):
self.dither_guide = value
t_ccd = AliasAttribute()
dither = AliasAttribute()
include_ids = AliasAttribute()
exclude_ids = AliasAttribute()

@property
def thumbs_up(self):
Expand Down Expand Up @@ -349,7 +340,6 @@ def get_initial_guide_candidates(self):
return cand_guides



def check_fid_trap(cand_stars, fids, dither):
"""
Search for guide stars that would cause the fid trap issue and mark as spoilers.
Expand All @@ -367,7 +357,7 @@ def check_fid_trap(cand_stars, fids, dither):
return spoilers, []

bad_row = GUIDE_CHAR.fid_trap['row']
bad_col= GUIDE_CHAR.fid_trap['col']
bad_col = GUIDE_CHAR.fid_trap['col']
fid_margin = GUIDE_CHAR.fid_trap['margin']

# Check to see if the fid is in the zone that's a problem for the trap and if there's
Expand Down
62 changes: 60 additions & 2 deletions proseco/tests/test_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def test_exception_handling():
t_ccd_acq=-10, t_ccd_guide=-10,
detector='ACIS-S', sim_offset=0, focus_offset=0,
n_guide=8, n_fid=3, n_acq=8,
include_ids=[1]) # Fail
include_ids_acq=[1]) # Fail
assert 'include_ids and include_halfws must have same length' in aca.exception

for obj in (aca, aca.acqs, aca.guides, aca.fids):
Expand Down Expand Up @@ -365,4 +365,62 @@ def test_dense_star_field_regress():

repr(aca) # Apply default formats
assert aca[TEST_COLS + ['mag']].pformat(max_width=-1) == exp



def test_aca_acqs_include_exclude():
"""
Test include and exclude stars. This uses a catalog with 11 stars:
- 8 bright stars from 7.0 to 7.7 mag, where the 7.0 is EXCLUDED
- 2 faint (but OK) stars 10.0, 10.1 where the 10.0 is INCLUDED
- 1 very faint (bad) stars 12.0 mag is INCLUDED
Both the 7.0 and 10.1 would normally get picked either initially
or swapped in during optimization, and 12.0 would never get picked.
Check that the final catalog is [7.1 .. 7.7, 10.0, 12.0]
This is a stripped down version of test_cand_acqs_include_exclude
in test_acq.py along with test_guides_include_exclude in test_guide.py.
"""
stars = StarsTable.empty()

stars.add_fake_constellation(mag=[7.0, 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 7.7],
id=[1, 2, 3, 4, 5, 6, 7, 8],
size=2000, n_stars=8)
stars.add_fake_constellation(mag=[10.0, 10.1, 12.0],
id=[9, 10, 11],
size=1500, n_stars=3)

# Put in a neighboring star that will keep star 9 out of the cand_acqs table
star9 = stars.get_id(9)
star9['ASPQ1'] = 20
stars.add_fake_star(yang=star9['yang'] + 20, zang=star9['zang'] + 20,
mag=star9['mag'] + 2.5, id=90)

# Define includes and excludes. id=9 is in nominal cand_acqs but not in acqs.
include_ids = [9, 11]
include_halfws = [45, 89]
exp_include_halfws = [60, 80]
exclude_ids = [1]

aca = get_aca_catalog(**STD_INFO, stars=stars,
include_ids_acq=include_ids,
include_halfws_acq=include_halfws,
exclude_ids_acq=exclude_ids,
include_ids_guide=include_ids,
exclude_ids_guide=exclude_ids)
acqs = aca.acqs
assert acqs.include_ids == include_ids
assert acqs.include_halfws == exp_include_halfws
assert acqs.exclude_ids == exclude_ids
assert all(id_ in acqs.cand_acqs['id'] for id_ in include_ids)

assert all(id_ in acqs['id'] for id_ in include_ids)
assert all(id_ not in acqs['id'] for id_ in exclude_ids)

assert np.all(acqs['id'] == [2, 3, 4, 5, 6, 7, 9, 11])
assert np.all(acqs['halfw'] == [160, 160, 160, 160, 160, 160, 60, 80])
assert np.allclose(acqs['mag'], [7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 10.0, 12.0])

guides = aca.guides
assert guides.include_ids == include_ids
assert guides.exclude_ids == exclude_ids
48 changes: 48 additions & 0 deletions proseco/tests/test_guide.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
check_mag_spoilers)
from ..characteristics_guide import mag_spoiler
from ..core import StarsTable
from .test_common import STD_INFO


HAS_SC_ARCHIVE = Path(mica.starcheck.starcheck.FILES['data_root']).exists()
Expand Down Expand Up @@ -236,3 +237,50 @@ def test_warnings():
guides = get_guide_catalog(att=(0, 0, 0), date='2018:001', t_ccd=-10, dither=(8, 8),
stars=stars, n_guide=8)
assert guides.warnings == ['WARNING: Selected only 6 guide stars versus requested 8']


def test_guides_include_exclude():
"""
Test include and exclude stars for guide. This uses a catalog with 11 stars:
- 8 bright stars from 7.0 to 7.7 mag, where the 7.0 is EXCLUDED
- 2 faint (but OK) stars 10.0, 10.1 where the 10.0 is INCLUDED
- 1 very faint (bad) stars 12.0 mag is INCLUDED
Both the 7.0 and 10.1 would normally get picked either initially
or swapped in during optimization, and 12.0 would never get picked.
NOTE: right now this is a stub that just checks that the include_ids
and exclude_ids get set accordingly. Later the include/exclude
functionality will be implemented and this test can be substantive.
"""
stars = StarsTable.empty()

stars.add_fake_constellation(mag=[7.0, 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 7.7],
id=[1, 2, 3, 4, 5, 6, 7, 8],
size=2000, n_stars=8)
stars.add_fake_constellation(mag=[10.0, 10.1, 12.0],
id=[9, 10, 11],
size=1500, n_stars=3)

# Make sure baseline catalog is working like expected
guides = get_guide_catalog(**STD_INFO, stars=stars)
assert np.all(guides['id'] == np.arange(1, 6))

# Define includes and excludes.
include_ids = [9, 11]
exclude_ids = [1]

guides = get_guide_catalog(**STD_INFO, stars=stars,
include_ids=include_ids,
exclude_ids=exclude_ids)

assert guides.include_ids == include_ids
assert guides.exclude_ids == exclude_ids

# assert all(id_ in guides.cand_guides['id'] for id_ in include_ids)

# assert all(id_ in guides['id'] for id_ in include_ids)
# assert all(id_ not in guides['id'] for id_ in exclude_ids)

# assert np.all(guides['id'] == [2, 3, 4, 5, 6, 7, 9, 11])
# assert np.allclose(guides['mag'], [7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 10.0, 12.0])

0 comments on commit 1c14d0e

Please sign in to comment.