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

1import numpy as np 

2from lsst.sims.featureScheduler.surveys import BaseSurvey 

3import copy 

4import lsst.sims.featureScheduler.basis_functions as basis_functions 

5from lsst.sims.featureScheduler.utils import empty_observation 

6from lsst.sims.featureScheduler import features 

7import logging 

8import random 

9 

10 

11__all__ = ['Deep_drilling_survey', 'generate_dd_surveys', 'dd_bfs', 'dd_u_bfs'] 

12 

13log = logging.getLogger(__name__) 

14 

15 

16class Deep_drilling_survey(BaseSurvey): 

17 """A survey class for running deep drilling fields. 

18 

19 Parameters 

20 ---------- 

21 basis_functions : list of lsst.sims.featureScheduler.basis_function objects 

22 These should be feasibility basis functions. 

23 RA : float 

24 The RA of the field (degrees) 

25 dec : float 

26 The dec of the field to observe (degrees) 

27 sequence : list of observation objects or str (rgizy) 

28 The sequence of observations to take. Can be a string of list of obs objects. 

29 nvis : list of ints 

30 The number of visits in each filter. Should be same length as sequence. 

31 survey_name : str (DD) 

32 The name to give this survey so it can be tracked 

33 reward_value : float (101.) 

34 The reward value to report if it is able to start (unitless). 

35 readtime : float (2.) 

36 Readout time for computing approximate time of observing the sequence. (seconds) 

37 filter_match_shuffle : bool (True) 

38 If True, switch up the order filters are executed in (first sequence will be currently 

39 loaded filter if possible) 

40 flush_pad : float (30.) 

41 How long to hold observations in the queue after they were expected to be completed (minutes). 

42 """ 

43 

44 def __init__(self, basis_functions, RA, dec, sequence='rgizy', 

45 nvis=[20, 10, 20, 26, 20], 

46 exptime=30., nexp=2, ignore_obs=None, survey_name='DD', 

47 reward_value=None, readtime=2., filter_change_time=120., 

48 nside=None, filter_match_shuffle=True, flush_pad=30., seed=42, detailers=None): 

49 super(Deep_drilling_survey, self).__init__(nside=nside, basis_functions=basis_functions, 

50 detailers=detailers, ignore_obs=ignore_obs) 

51 random.seed(a=seed) 

52 

53 self.ra = np.radians(RA) 

54 self.ra_hours = RA/360.*24. 

55 self.dec = np.radians(dec) 

56 self.survey_name = survey_name 

57 self.reward_value = reward_value 

58 self.flush_pad = flush_pad/60./24. # To days 

59 self.filter_sequence = [] 

60 if type(sequence) == str: 

61 self.observations = [] 

62 for num, filtername in zip(nvis, sequence): 

63 for j in range(num): 

64 obs = empty_observation() 

65 obs['filter'] = filtername 

66 obs['exptime'] = exptime 

67 obs['RA'] = self.ra 

68 obs['dec'] = self.dec 

69 obs['nexp'] = nexp 

70 obs['note'] = survey_name 

71 self.observations.append(obs) 

72 self.filter_sequence.append(filtername) 

73 else: 

74 self.observations = sequence 

75 self.filter_sequence = [obs['filter'] for obs in sequence] 

76 

77 # Make an estimate of how long a seqeunce will take. Assumes no major rotational or spatial 

78 # dithering slowing things down. 

79 self.approx_time = np.sum([o['exptime']+readtime*o['nexp'] for o in self.observations])/3600./24. \ 

80 + filter_change_time*len(sequence)/3600./24. # to days 

81 self.filter_match_shuffle = filter_match_shuffle 

82 self.filter_indices = {} 

83 self.filter_sequence = np.array(self.filter_sequence) 

84 for filtername in np.unique(self.filter_sequence): 

85 self.filter_indices[filtername] = np.where(self.filter_sequence == filtername)[0] 

86 

87 if self.reward_value is None: 

88 self.extra_features['Ntot'] = features.N_obs_survey() 

89 self.extra_features['N_survey'] = features.N_obs_survey(note=self.survey_name) 

90 

91 def check_continue(self, observation, conditions): 

92 # feasibility basis functions? 

93 ''' 

94 This method enables external calls to check if a given observations that belongs to this survey is 

95 feasible or not. This is called once a sequence has started to make sure it can continue. 

96 

97 XXX--TODO: Need to decide if we want to develope check_continue, or instead hold the 

98 sequence in the survey, and be able to check it that way. 

99 ''' 

100 

101 result = True 

102 

103 return result 

104 

105 def calc_reward_function(self, conditions): 

106 result = -np.inf 

107 if self._check_feasibility(conditions): 

108 if self.reward_value is not None: 

109 result = self.reward_value 

110 else: 

111 # XXX This might backfire if we want to have DDFs with different fractions of the 

112 # survey time. Then might need to define a goal fraction, and have the reward be the 

113 # number of observations behind that target fraction. 

114 result = self.extra_features['Ntot'].feature / (self.extra_features['N_survey'].feature+1) 

115 return result 

116 

117 def generate_observations_rough(self, conditions): 

118 result = [] 

119 if self._check_feasibility(conditions): 

120 result = copy.deepcopy(self.observations) 

121 

122 if self.filter_match_shuffle: 

123 filters_remaining = list(self.filter_indices.keys()) 

124 random.shuffle(filters_remaining) 

125 # If we want to observe the currrent filter, put it first 

126 if conditions.current_filter in filters_remaining: 

127 filters_remaining.insert(0, filters_remaining.pop(filters_remaining.index(conditions.current_filter))) 

128 final_result = [] 

129 for filtername in filters_remaining: 

130 final_result.extend(result[np.min(self.filter_indices[filtername]):np.max(self.filter_indices[filtername])+1]) 

131 result = final_result 

132 # Let's set the mjd to flush the queue by 

133 for i, obs in enumerate(result): 

134 result[i]['flush_by_mjd'] = conditions.mjd + self.approx_time + self.flush_pad 

135 return result 

136 

137 

138def dd_bfs(RA, dec, survey_name, ha_limits, frac_total=0.0185, aggressive_frac=0.011): 

139 """ 

140 Convienence function to generate all the feasibility basis functions 

141 """ 

142 sun_alt_limit = -18. 

143 time_needed = 62. 

144 fractions = [0.00, aggressive_frac, frac_total] 

145 bfs = [] 

146 bfs.append(basis_functions.Filter_loaded_basis_function(filternames=['r', 'g', 'i', 'z', 'y'])) 

147 bfs.append(basis_functions.Not_twilight_basis_function(sun_alt_limit=sun_alt_limit)) 

148 bfs.append(basis_functions.Time_to_twilight_basis_function(time_needed=time_needed)) 

149 bfs.append(basis_functions.Hour_Angle_limit_basis_function(RA=RA, ha_limits=ha_limits)) 

150 bfs.append(basis_functions.Fraction_of_obs_basis_function(frac_total=frac_total, survey_name=survey_name)) 

151 bfs.append(basis_functions.Look_ahead_ddf_basis_function(frac_total, aggressive_frac, 

152 sun_alt_limit=sun_alt_limit, time_needed=time_needed, 

153 RA=RA, survey_name=survey_name, 

154 ha_limits=ha_limits)) 

155 bfs.append(basis_functions.Soft_delay_basis_function(fractions=fractions, delays=[0., 0.5, 1.5], 

156 survey_name=survey_name)) 

157 

158 return bfs 

159 

160 

161def dd_u_bfs(RA, dec, survey_name, ha_limits, frac_total=0.0019, aggressive_frac=0.0014): 

162 """Convienence function to generate all the feasibility basis functions for u-band DDFs 

163 """ 

164 bfs = [] 

165 sun_alt_limit = -18. 

166 time_needed = 6. 

167 fractions = [0.00, aggressive_frac, frac_total] 

168 bfs.append(basis_functions.Filter_loaded_basis_function(filternames='u')) 

169 bfs.append(basis_functions.Not_twilight_basis_function(sun_alt_limit=sun_alt_limit)) 

170 bfs.append(basis_functions.Time_to_twilight_basis_function(time_needed=time_needed)) 

171 bfs.append(basis_functions.Hour_Angle_limit_basis_function(RA=RA, ha_limits=ha_limits)) 

172 bfs.append(basis_functions.Moon_down_basis_function()) 

173 bfs.append(basis_functions.Fraction_of_obs_basis_function(frac_total=frac_total, survey_name=survey_name)) 

174 bfs.append(basis_functions.Look_ahead_ddf_basis_function(frac_total, aggressive_frac, 

175 sun_alt_limit=sun_alt_limit, time_needed=time_needed, 

176 RA=RA, survey_name=survey_name, 

177 ha_limits=ha_limits)) 

178 bfs.append(basis_functions.Soft_delay_basis_function(fractions=fractions, delays=[0., 0.2, 0.5], 

179 survey_name=survey_name)) 

180 

181 return bfs 

182 

183 

184def generate_dd_surveys(nside=None, nexp=2, detailers=None, reward_value=100): 

185 """Utility to return a list of standard deep drilling field surveys. 

186 

187 XXX-Someone double check that I got the coordinates right! 

188 

189 """ 

190 

191 surveys = [] 

192 

193 # ELAIS S1 

194 RA = 9.45 

195 dec = -44. 

196 survey_name = 'DD:ELAISS1' 

197 ha_limits = ([0., 1.5], [22.5, 24.]) 

198 bfs = dd_bfs(RA, dec, survey_name, ha_limits) 

199 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence='rgizy', 

200 nvis=[20, 10, 20, 26, 20], 

201 survey_name=survey_name, reward_value=reward_value, 

202 nside=nside, nexp=nexp, detailers=detailers)) 

203 

204 survey_name = 'DD:u,ELAISS1' 

205 bfs = dd_u_bfs(RA, dec, survey_name, ha_limits) 

206 

207 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence='u', 

208 nvis=[8], survey_name=survey_name, reward_value=reward_value, nside=nside, 

209 nexp=nexp, detailers=detailers)) 

210 

211 # XMM-LSS 

212 survey_name = 'DD:XMM-LSS' 

213 RA = 35.708333 

214 dec = -4-45/60. 

215 ha_limits = ([0., 1.5], [22.5, 24.]) 

216 bfs = dd_bfs(RA, dec, survey_name, ha_limits) 

217 

218 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence='rgizy', 

219 nvis=[20, 10, 20, 26, 20], survey_name=survey_name, reward_value=reward_value, 

220 nside=nside, nexp=nexp, detailers=detailers)) 

221 survey_name = 'DD:u,XMM-LSS' 

222 bfs = dd_u_bfs(RA, dec, survey_name, ha_limits) 

223 

224 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence='u', 

225 nvis=[8], survey_name=survey_name, reward_value=reward_value, nside=nside, 

226 nexp=nexp, detailers=detailers)) 

227 

228 # Extended Chandra Deep Field South 

229 # XXX -- this one can pass too close to zenith 

230 RA = 53.125 

231 dec = -28.-6/60. 

232 survey_name = 'DD:ECDFS' 

233 ha_limits = [[0.5, 3.0], [20., 22.5]] 

234 bfs = dd_bfs(RA, dec, survey_name, ha_limits) 

235 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence='rgizy', 

236 nvis=[20, 10, 20, 26, 20], 

237 survey_name=survey_name, reward_value=reward_value, nside=nside, 

238 nexp=nexp, detailers=detailers)) 

239 

240 survey_name = 'DD:u,ECDFS' 

241 bfs = dd_u_bfs(RA, dec, survey_name, ha_limits) 

242 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence='u', 

243 nvis=[8], survey_name=survey_name, reward_value=reward_value, nside=nside, 

244 nexp=nexp, detailers=detailers)) 

245 # COSMOS 

246 RA = 150.1 

247 dec = 2.+10./60.+55/3600. 

248 survey_name = 'DD:COSMOS' 

249 ha_limits = ([0., 1.5], [22.5, 24.]) 

250 bfs = dd_bfs(RA, dec, survey_name, ha_limits) 

251 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence='rgizy', 

252 nvis=[20, 10, 20, 26, 20], 

253 survey_name=survey_name, reward_value=reward_value, nside=nside, 

254 nexp=nexp, detailers=detailers)) 

255 survey_name = 'DD:u,COSMOS' 

256 bfs = dd_u_bfs(RA, dec, survey_name, ha_limits) 

257 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence='u', 

258 nvis=[8], survey_name=survey_name, reward_value=reward_value, nside=nside, 

259 nexp=nexp, detailers=detailers)) 

260 

261 # Extra DD Field, just to get to 5. Still not closed on this one 

262 survey_name = 'DD:290' 

263 RA = 349.386443 

264 dec = -63.321004 

265 ha_limits = ([0., 1.5], [22.5, 24.]) 

266 bfs = dd_bfs(RA, dec, survey_name, ha_limits) 

267 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence='rgizy', 

268 nvis=[20, 10, 20, 26, 20], 

269 survey_name=survey_name, reward_value=reward_value, nside=nside, 

270 nexp=nexp, detailers=detailers)) 

271 

272 survey_name = 'DD:u,290' 

273 bfs = dd_u_bfs(RA, dec, survey_name, ha_limits) 

274 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence='u', nvis=[8], 

275 survey_name=survey_name, reward_value=reward_value, nside=nside, 

276 nexp=nexp, detailers=detailers)) 

277 

278 return surveys