Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1from lsst.sims.utils import _raDec2Hpid, _approx_RaDec2AltAz, _angularSeparation, _approx_altaz2pa 

2import numpy as np 

3from lsst.sims.featureScheduler.utils import int_rounded 

4import copy 

5 

6__all__ = ["Base_detailer", "Zero_rot_detailer", "Comcam_90rot_detailer", "Close_alt_detailer", 

7 "Take_as_pairs_detailer", "Twilight_triple_detailer", "Spider_rot_detailer"] 

8 

9 

10class Base_detailer(object): 

11 """ 

12 A Detailer is an object that takes a list of proposed observations and adds "details" to them. The 

13 primary purpose is that the Markov Decision Process does an excelent job selecting RA,Dec,filter 

14 combinations, but we may want to add additional logic such as what to set the camera rotation angle 

15 to, or what to use for an exposure time. We could also modify the order of the proposed observations. 

16 For Deep Drilling Fields, a detailer could be useful for computing dither positions and modifying 

17 the exact RA,Dec positions. 

18 """ 

19 

20 def __init__(self, nside=32): 

21 """ 

22 """ 

23 # Dict to hold all the features we want to track 

24 self.survey_features = {} 

25 self.nside = nside 

26 

27 def add_observation(self, observation, indx=None): 

28 """ 

29 Parameters 

30 ---------- 

31 observation : np.array 

32 An array with information about the input observation 

33 indx : np.array 

34 The indices of the healpix map that the observation overlaps with 

35 """ 

36 for feature in self.survey_features: 

37 self.survey_features[feature].add_observation(observation, indx=indx) 

38 

39 def __call__(self, observation_list, conditions): 

40 """ 

41 Parameters 

42 ---------- 

43 observation_list : list of observations 

44 The observations to detail. 

45 conditions : lsst.sims.featureScheduler.conditions object 

46 

47 Returns 

48 ------- 

49 List of observations. 

50 """ 

51 

52 return observation_list 

53 

54 

55class Zero_rot_detailer(Base_detailer): 

56 """ 

57 Detailer to set the camera rotation to be apporximately zero in rotTelPos. 

58 Because it can never be written too many times: 

59 rotSkyPos = rotTelPos - ParallacticAngle 

60 But, wait, what? Is it really the other way? 

61 """ 

62 

63 def __call__(self, observation_list, conditions): 

64 

65 # XXX--should I convert the list into an array and get rid of this loop? 

66 for obs in observation_list: 

67 alt, az = _approx_RaDec2AltAz(obs['RA'], obs['dec'], conditions.site.latitude_rad, 

68 conditions.site.longitude_rad, conditions.mjd) 

69 obs_pa = _approx_altaz2pa(alt, az, conditions.site.latitude_rad) 

70 obs['rotSkyPos'] = obs_pa 

71 

72 return observation_list 

73 

74 

75class Spider_rot_detailer(Base_detailer): 

76 """ 

77 Set the camera rotation to +/- 45 degrees so diffraction spikes align along chip rows 

78 and columns 

79 """ 

80 

81 def __call__(self, observation_list, conditions): 

82 indx = int(conditions.night % 2) 

83 rotTelPos = np.radians([45., 315.][indx]) 

84 

85 for obs in observation_list: 

86 obs['rotSkyPos'] = np.nan 

87 obs['rotTelPos'] = rotTelPos 

88 

89 return observation_list 

90 

91 

92class Comcam_90rot_detailer(Base_detailer): 

93 """ 

94 Detailer to set the camera rotation so rotSkyPos is 0, 90, 180, or 270 degrees. Whatever 

95 is closest to rotTelPos of zero. 

96 """ 

97 

98 def __call__(self, observation_list, conditions): 

99 favored_rotSkyPos = np.radians([0., 90., 180., 270., 360.]).reshape(5, 1) 

100 obs_array =np.concatenate(observation_list) 

101 alt, az = _approx_RaDec2AltAz(obs_array['RA'], obs_array['dec'], conditions.site.latitude_rad, 

102 conditions.site.longitude_rad, conditions.mjd) 

103 parallactic_angle = _approx_altaz2pa(alt, az, conditions.site.latitude_rad) 

104 # If we set rotSkyPos to parallactic angle, rotTelPos will be zero. So, find the 

105 # favored rotSkyPos that is closest to PA to keep rotTelPos as close as possible to zero. 

106 ang_diff = np.abs(parallactic_angle - favored_rotSkyPos) 

107 min_indxs = np.argmin(ang_diff, axis=0) 

108 # can swap 360 and zero if needed? 

109 final_rotSkyPos = favored_rotSkyPos[min_indxs] 

110 # Set all the observations to the proper rotSkyPos 

111 for rsp, obs in zip(final_rotSkyPos, observation_list): 

112 obs['rotSkyPos'] = rsp 

113 

114 return observation_list 

115 

116 

117class Close_alt_detailer(Base_detailer): 

118 """ 

119 re-order a list of observations so that the closest in altitude to the current pointing is first. 

120 

121 Parameters 

122 ---------- 

123 alt_band : float (10) 

124 The altitude band to try and stay in (degrees) 

125 """ 

126 def __init__(self, alt_band=10.): 

127 super(Close_alt_detailer, self).__init__() 

128 self.alt_band = int_rounded(np.radians(alt_band)) 

129 

130 def __call__(self, observation_list, conditions): 

131 obs_array = np.concatenate(observation_list) 

132 alt, az = _approx_RaDec2AltAz(obs_array['RA'], obs_array['dec'], conditions.site.latitude_rad, 

133 conditions.site.longitude_rad, conditions.mjd) 

134 alt_diff = np.abs(alt - conditions.telAlt) 

135 in_band = np.where(int_rounded(alt_diff) <= self.alt_band)[0] 

136 if in_band.size == 0: 

137 in_band = np.arange(alt.size) 

138 

139 # Find the closest in angular distance of the points that are in band 

140 ang_dist = _angularSeparation(az[in_band], alt[in_band], conditions.telAz, conditions.telAlt) 

141 good = np.min(np.where(ang_dist == ang_dist.min())[0]) 

142 indx = in_band[good] 

143 result = observation_list[indx:] + observation_list[:indx] 

144 return result 

145 

146 

147class Take_as_pairs_detailer(Base_detailer): 

148 def __init__(self, filtername='r'): 

149 """ 

150 """ 

151 super(Take_as_pairs_detailer, self).__init__() 

152 self.filtername = filtername 

153 

154 def __call__(self, observation_list, conditions): 

155 paired = copy.deepcopy(observation_list) 

156 for obs in paired: 

157 obs['filter'] = self.filtername 

158 if conditions.current_filter == self.filtername: 

159 for obs in paired: 

160 obs['note'] = obs['note'][0] + ', a' 

161 for obs in observation_list: 

162 obs['note'] = obs['note'][0] + ', b' 

163 result = paired + observation_list 

164 else: 

165 for obs in paired: 

166 obs['note'] = obs['note'][0] + ', b' 

167 for obs in observation_list: 

168 obs['note'] = obs['note'][0] + ', a' 

169 result = observation_list + paired 

170 # XXX--maybe a temp debugging thing, label what part of sequence each observation is. 

171 for i, obs in enumerate(result): 

172 obs['survey_id'] = i 

173 return result 

174 

175 

176class Twilight_triple_detailer(Base_detailer): 

177 def __init__(self, slew_estimate=5.0, n_repeat=3): 

178 super(Twilight_triple_detailer, self).__init__() 

179 self.slew_estimate = slew_estimate 

180 self.n_repeat = n_repeat 

181 

182 def __call__(self, observation_list, conditions): 

183 

184 obs_array = np.concatenate(observation_list) 

185 

186 # Estimate how much time is left in the twilgiht block 

187 potential_times = np.array([conditions.sun_n18_setting - conditions.mjd, 

188 conditions.sun_n12_rising - conditions.mjd]) 

189 

190 potential_times = np.min(potential_times[np.where(potential_times > 0)]) * 24.*3600. 

191 

192 # How long will observations take? 

193 cumulative_slew = np.arange(obs_array.size) * self.slew_estimate 

194 cumulative_expt = np.cumsum(obs_array['exptime']) 

195 cumulative_time = cumulative_slew + cumulative_expt 

196 # If we are way over, truncate the list before doing the triple 

197 if np.max(cumulative_time) > potential_times: 

198 max_indx = np.where(cumulative_time/self.n_repeat <= potential_times)[0] 

199 if np.size(max_indx) == 0: 

200 # Very bad magic number fudge 

201 max_indx = 3 

202 else: 

203 max_indx = np.max(max_indx) 

204 if max_indx == 0: 

205 max_indx += 1 

206 observation_list = observation_list[0:max_indx] 

207 

208 # Repeat the observations n times 

209 out_obs = [] 

210 for i in range(self.n_repeat): 

211 out_obs.extend(copy.deepcopy(observation_list)) 

212 

213 return out_obs