Skip to content

Commit 5e90dc0

Browse files
authored
Merge pull request #27 from sdss/offset_flag_fix
Offset flag fix
2 parents 011dd45 + 7f4a9d2 commit 5e90dc0

File tree

2 files changed

+151
-22
lines changed

2 files changed

+151
-22
lines changed

src/coordio/utils.py

Lines changed: 102 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -947,9 +947,81 @@ def offset_definition(mag, mag_limits, lunation, waveName, obsSite, fmagloss=Non
947947
return r, offset_flag
948948

949949

950+
def offset_valid_check(mags, mag_limits, offset_flag, program):
951+
"""
952+
check if the offset returned is a valid value or not for a set of targets
953+
954+
Parameters
955+
----------
956+
mags: numpy.array
957+
The magniutdes of the objects. Should be a 2D, Nx10 array, where
958+
N is number of objects and length of 10 index should correspond
959+
to magntidues: [g, r, i, z, bp, gaia_g, rp, J, H, K].
960+
961+
mag_limits: numpy.array
962+
Magnitude limits for the designmode of the design.
963+
This should be an array of length N=10 where indexes
964+
correspond to magntidues: [g, r, i, z, bp, gaia_g, rp, J, H, K].
965+
This matches the apogee_bright_limit_targets_min or
966+
boss_bright_limit_targets_min (depending on instrument) from
967+
targetdb.DesignMode for the design_mode of the design.
968+
969+
offset_flag: numpy.array
970+
bitmask for how offset was set in
971+
numpy array of length N. Flags are:
972+
- 0: offset applied normally (i.e. when mag <= mag_limit)
973+
- 1: no offset applied because mag > mag_limit
974+
- 2: no offset applied because magnitude was null value.
975+
- 8: offsets should not be used as sky brightness is <=
976+
minimum offset sky brightness
977+
- 16: no offsets applied because can_offset = False
978+
- 32: no offset applied because mag <= offset_bright_limit
979+
(offset_bright_limit is G = 6 for Boss bright time and
980+
G = 13 for Boss dark time, and
981+
H = 1 for Apogee).
982+
- 64: no offset applied because no valid magnitude limits
983+
984+
program: numpy.array
985+
The program for each object
986+
987+
Returns
988+
---------
989+
offset_valid: numpy.array
990+
Boolean array if the offset is valid or not
991+
"""
992+
# make bad mag cases nan
993+
cases = [-999, -9999, 999, 0.0, numpy.nan, 99.9, None]
994+
mags[numpy.isin(mags, cases)] = numpy.nan
995+
996+
# check stars that are too bright for design mode
997+
mag_limits = numpy.array(mag_limits)
998+
valid_ind = numpy.where(numpy.array(mag_limits) != -999.0)[0]
999+
mag_bright = numpy.any(mags[:, valid_ind] < mag_limits[valid_ind], axis=1)
1000+
1001+
# check offset flags to see if should be used or not
1002+
offset_valid = numpy.zeros(len(mags), dtype=bool)
1003+
for i, fl in enumerate(offset_flag):
1004+
# manually check bad flags
1005+
if program[i] == "SKY" or "ops" in program[i]:
1006+
offset_valid[i] = True
1007+
elif 8 & int(fl) and mag_bright[i]:
1008+
# if below sky brightness and brighter than mag limit
1009+
offset_valid[i] = False
1010+
elif 16 & int(fl) and mag_bright[i]:
1011+
# if can_offset False and brighter than mag limit
1012+
offset_valid[i] = False
1013+
elif 32 & int(fl):
1014+
# if brighter than safety limit
1015+
offset_valid[i] = False
1016+
else:
1017+
offset_valid[i] = True
1018+
return offset_valid
1019+
1020+
9501021
def object_offset(mags, mag_limits, lunation, waveName, obsSite, fmagloss=None,
9511022
safety_factor=None, beta=None, FWHM=None, skybrightness=None,
952-
offset_min_skybrightness=None, can_offset=None):
1023+
offset_min_skybrightness=None, can_offset=None,
1024+
check_valid_offset=False, program=None):
9531025
"""
9541026
Returns the offset needed for object with mag to be
9551027
observed at mag_limit. Currently assumption is all offsets
@@ -1008,6 +1080,14 @@ def object_offset(mags, mag_limits, lunation, waveName, obsSite, fmagloss=None,
10081080
can_offset value from targetdb for the target(s) to be
10091081
offset. Only set if
10101082
want to check for offset_flag NO_CAN_OFFSET (16).
1083+
1084+
check_valid_offset: boolean
1085+
Whether or not to check for valid offsets. Program must be
1086+
set for this to be returned as well.
1087+
1088+
program: numpy.array
1089+
Program of the targets being offset. Needed for valid offset
1090+
check.
10111091
10121092
Returns
10131093
-------
@@ -1033,6 +1113,10 @@ def object_offset(mags, mag_limits, lunation, waveName, obsSite, fmagloss=None,
10331113
G = 13 for Boss dark time, and
10341114
H = 1 for Apogee).
10351115
- 64: no offset applied because no valid magnitude limits
1116+
1117+
offset_valid: numpy.array
1118+
Boolean array if the offset is valid or not. Only returned if
1119+
check_valid_offset = True and program is not None
10361120
"""
10371121
# check if 2D array
10381122
if len(mags.shape) != 2:
@@ -1076,9 +1160,12 @@ def unique_offset_flags(flags):
10761160
total_flags = numpy.sum(numpy.unique(unq_flags))
10771161
return numpy.zeros(flags.shape) + total_flags
10781162
try:
1079-
offset_flags[numpy.all(delta_ras == 0., 1)] = numpy.apply_along_axis(unique_offset_flags,
1080-
1,
1081-
offset_flags[numpy.all(delta_ras == 0., 1)])
1163+
# catch all where entire row is == 0 or flag 32 thrown
1164+
# need to remove flag 32 things
1165+
ev_off = (numpy.all(delta_ras == 0., 1)) | (numpy.any(offset_flags == 32, 1))
1166+
offset_flags[ev_off] = numpy.apply_along_axis(unique_offset_flags,
1167+
1,
1168+
offset_flags[ev_off])
10821169
except ValueError:
10831170
pass
10841171
# use max offset
@@ -1087,7 +1174,17 @@ def unique_offset_flags(flags):
10871174
offset_flag = numpy.array([offset_flags[i, j] for i, j in enumerate(ind_max)],
10881175
dtype=int)
10891176
delta_dec = numpy.zeros(len(delta_ra))
1090-
return delta_ra, delta_dec, offset_flag
1177+
1178+
# zero out things with flag 32 in them but delta_ra > 0
1179+
flag_32 = numpy.array([32 & int(fl) for fl in offset_flag], dtype=bool)
1180+
delta_ra[(delta_ra > 0) & flag_32] = 0.
1181+
if check_valid_offset:
1182+
if program is None:
1183+
raise ValueError('Must provide program to check valid offsets!')
1184+
offset_valid = offset_valid_check(mags, mag_limits, offset_flag, program)
1185+
return delta_ra, delta_dec, offset_flag, offset_valid
1186+
else:
1187+
return delta_ra, delta_dec, offset_flag
10911188

10921189
def _offset_radec(ra=None, dec=None, delta_ra=0., delta_dec=0.):
10931190
"""Offsets ra and dec according to specified amount. From Mike's

tests/test_offset.py

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
def test_all_flags():
1919
def test_flags(flag, mag, mag_limits, lunation, waveName,
20-
sky, offset_min_skybrightness, can_off):
20+
sky, offset_min_skybrightness, can_off, offset_val):
2121
# test APO fails with 1D array
2222
with pytest.raises(ValueError, match='mags must be a 2D numpy.array of shape \\(N, 10\\)'):
2323
delta_ra, delta_dec, offset_flag = object_offset(mag, mag_limits, lunation,
@@ -61,12 +61,37 @@ def test_flags(flag, mag, mag_limits, lunation, waveName,
6161
assert numpy.all(delta_ra == 0.)
6262
else:
6363
assert numpy.all(delta_ra > 0.)
64+
65+
# check offset_valid
66+
with pytest.raises(ValueError, match='Must provide program to check valid offsets!'):
67+
delta_ra, delta_dec, offset_flag, valid_offset = object_offset(
68+
numpy.vstack((mag, mag)), mag_limits, lunation,
69+
waveName, 'APO', fmagloss=fmagloss, skybrightness=sky,
70+
offset_min_skybrightness=offset_min_skybrightness,
71+
can_offset=can_off_arr,
72+
check_valid_offset=True)
73+
delta_ra, delta_dec, offset_flag, valid_offset = object_offset(
74+
numpy.vstack((mag, mag)), mag_limits, lunation,
75+
waveName, 'APO', fmagloss=fmagloss, skybrightness=sky,
76+
offset_min_skybrightness=offset_min_skybrightness,
77+
can_offset=can_off_arr,
78+
check_valid_offset=True,
79+
program=numpy.array(['science',
80+
'science']))
81+
assert numpy.all(offset_flag == flag)
82+
assert numpy.all(delta_dec == 0.)
83+
if flag > 0:
84+
assert numpy.all(delta_ra == 0.)
85+
else:
86+
assert numpy.all(delta_ra > 0.)
87+
assert numpy.all(valid_offset == offset_val)
6488
# Boss Bright
6589
offset_min_skybrightness = 1
6690
waveName = 'Boss'
6791
lunation = 'bright'
68-
flags_test = [0, 1, 2, 8, 16, 32]
92+
flags_test = [0, 1, 2, 8, 16, 32, 32]
6993
flags_test = [f if f == 0 else f + 64 for f in flags_test]
94+
offset_valid = [True, True, True, False, False, False, False]
7095

7196
test_mags = []
7297
test_mags.append(numpy.array([m - 2 if m != -999. else m for m in mag_limits[lunation][waveName]]))
@@ -75,19 +100,22 @@ def test_flags(flag, mag, mag_limits, lunation, waveName,
75100
test_mags.append(numpy.array([m - 2 if m != -999. else m for m in mag_limits[lunation][waveName]]))
76101
test_mags.append(numpy.array([m - 2 if m != -999. else m for m in mag_limits[lunation][waveName]]))
77102
test_mags.append(numpy.array([5. if m != -999. else m for m in mag_limits[lunation][waveName]]))
103+
test_mags.append(numpy.array([7. if m != -999. else m for m in mag_limits[lunation][waveName]]))
104+
test_mags[-1][0] = 5.
78105

79-
skybrightness = [None, None, None, 0.3, None, None]
80-
can_offset = [None, None, None, None, False, None]
81-
for flag, mag, sky, can_off in zip(flags_test, test_mags, skybrightness, can_offset):
106+
skybrightness = [None, None, None, 0.3, None, None, None]
107+
can_offset = [None, None, None, None, False, None, None]
108+
for flag, mag, sky, can_off, offset_val in zip(flags_test, test_mags, skybrightness, can_offset, offset_valid):
82109
test_flags(flag, mag, mag_limits[lunation][waveName], lunation, waveName,
83-
sky, offset_min_skybrightness, can_off)
110+
sky, offset_min_skybrightness, can_off, offset_val)
84111

85112
# Boss dark
86113
offset_min_skybrightness = 1
87114
waveName = 'Boss'
88115
lunation = 'dark'
89-
flags_test = [0, 1, 2, 8, 16, 32]
116+
flags_test = [0, 1, 2, 8, 16, 32, 32]
90117
flags_test = [f if f == 0 else f + 64 for f in flags_test]
118+
offset_valid = [True, True, True, False, False, False, False]
91119

92120
test_mags = []
93121
test_mags.append(numpy.array([m - 1 if m != -999. else m for m in mag_limits[lunation][waveName]]))
@@ -96,19 +124,22 @@ def test_flags(flag, mag, mag_limits, lunation, waveName,
96124
test_mags.append(numpy.array([m - 1 if m != -999. else m for m in mag_limits[lunation][waveName]]))
97125
test_mags.append(numpy.array([m - 1 if m != -999. else m for m in mag_limits[lunation][waveName]]))
98126
test_mags.append(numpy.array([12. if m != -999. else m for m in mag_limits[lunation][waveName]]))
127+
test_mags.append(numpy.array([14. if m != -999. else m for m in mag_limits[lunation][waveName]]))
128+
test_mags[-1][0] = 12.
99129

100-
skybrightness = [None, None, None, 0.3, None, None]
101-
can_offset = [None, None, None, None, False, None]
102-
for flag, mag, sky, can_off in zip(flags_test, test_mags, skybrightness, can_offset):
130+
skybrightness = [None, None, None, 0.3, None, None, None]
131+
can_offset = [None, None, None, None, False, None, None]
132+
for flag, mag, sky, can_off, offset_val in zip(flags_test, test_mags, skybrightness, can_offset, offset_valid):
103133
test_flags(flag, mag, mag_limits[lunation][waveName], lunation, waveName,
104-
sky, offset_min_skybrightness, can_off)
134+
sky, offset_min_skybrightness, can_off, offset_val)
105135

106136
# Apogee bright
107137
offset_min_skybrightness = 1
108138
waveName = 'Apogee'
109139
lunation = 'bright'
110140
flags_test = [0, 1, 2, 8, 16, 32]
111141
flags_test = [f if f == 0 else f + 64 for f in flags_test]
142+
offset_valid = [True, True, True, False, False, False]
112143

113144
test_mags = []
114145
test_mags.append(numpy.array([m - 2 if m != -999. else m for m in mag_limits[lunation][waveName]]))
@@ -120,16 +151,17 @@ def test_flags(flag, mag, mag_limits, lunation, waveName,
120151

121152
skybrightness = [None, None, None, 0.3, None, None]
122153
can_offset = [None, None, None, None, False, None]
123-
for flag, mag, sky, can_off in zip(flags_test, test_mags, skybrightness, can_offset):
154+
for flag, mag, sky, can_off, offset_val in zip(flags_test, test_mags, skybrightness, can_offset, offset_valid):
124155
test_flags(flag, mag, mag_limits[lunation][waveName], lunation, waveName,
125-
sky, offset_min_skybrightness, can_off)
156+
sky, offset_min_skybrightness, can_off, offset_val)
126157

127158
# Apogee dark
128159
offset_min_skybrightness = 1
129160
waveName = 'Apogee'
130161
lunation = 'dark'
131162
flags_test = [0, 1, 2, 8, 16, 32]
132163
flags_test = [f if f == 0 else f + 64 for f in flags_test]
164+
offset_valid = [True, True, True, False, False, False]
133165

134166
test_mags = []
135167
test_mags.append(numpy.array([m - 2 if m != -999. else m for m in mag_limits[lunation][waveName]]))
@@ -141,9 +173,9 @@ def test_flags(flag, mag, mag_limits, lunation, waveName,
141173

142174
skybrightness = [None, None, None, 0.3, None, None]
143175
can_offset = [None, None, None, None, False, None]
144-
for flag, mag, sky, can_off in zip(flags_test, test_mags, skybrightness, can_offset):
176+
for flag, mag, sky, can_off, offset_val in zip(flags_test, test_mags, skybrightness, can_offset, offset_valid):
145177
test_flags(flag, mag, mag_limits[lunation][waveName], lunation, waveName,
146-
sky, offset_min_skybrightness, can_off)
178+
sky, offset_min_skybrightness, can_off, offset_val)
147179

148180
# test engineering design_mode
149181
# Boss Bright
@@ -159,7 +191,7 @@ def test_flags(flag, mag, mag_limits, lunation, waveName,
159191
can_offset = [None]
160192
for flag, mag, sky, can_off in zip(flags_test, test_mags, skybrightness, can_offset):
161193
test_flags(flag, mag, numpy.zeros(10) - 999., lunation, waveName,
162-
sky, offset_min_skybrightness, can_off)
194+
sky, offset_min_skybrightness, can_off, True)
163195

164196
# test get combination of all flags
165197
# Boss Bright
@@ -177,7 +209,7 @@ def test_flags(flag, mag, mag_limits, lunation, waveName,
177209
can_offset = [None]
178210
for flag, mag, sky, can_off in zip(flags_test, test_mags, skybrightness, can_offset):
179211
test_flags(flag, mag, mag_limits[lunation][waveName], lunation, waveName,
180-
sky, offset_min_skybrightness, can_off)
212+
sky, offset_min_skybrightness, can_off, False)
181213

182214

183215
# test bright neighbor exclusion radius for very bright stars

0 commit comments

Comments
 (0)