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 # Toss any filters that are not currently loaded 

123 result = [obs for obs in result if obs['filter'] in conditions.mounted_filters] 

124 

125 if self.filter_match_shuffle: 

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

127 random.shuffle(filters_remaining) 

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

129 if conditions.current_filter in filters_remaining: 

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

131 final_result = [] 

132 for filtername in filters_remaining: 

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

134 result = final_result 

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

136 for i, obs in enumerate(result): 

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

138 return result 

139 

140 

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

142 """ 

143 Convienence function to generate all the feasibility basis functions 

144 """ 

145 sun_alt_limit = -18. 

146 time_needed = 62. 

147 fractions = [0.00, aggressive_frac, frac_total] 

148 bfs = [] 

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

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

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

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

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

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

155 sun_alt_limit=sun_alt_limit, time_needed=time_needed, 

156 RA=RA, survey_name=survey_name, 

157 ha_limits=ha_limits)) 

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

159 survey_name=survey_name)) 

160 

161 return bfs 

162 

163 

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

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

166 """ 

167 bfs = [] 

168 sun_alt_limit = -18. 

169 time_needed = 6. 

170 fractions = [0.00, aggressive_frac, frac_total] 

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

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

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

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

175 bfs.append(basis_functions.Moon_down_basis_function()) 

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

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

178 sun_alt_limit=sun_alt_limit, time_needed=time_needed, 

179 RA=RA, survey_name=survey_name, 

180 ha_limits=ha_limits)) 

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

182 survey_name=survey_name)) 

183 

184 return bfs 

185 

186 

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

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

189 

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

191 

192 """ 

193 

194 surveys = [] 

195 

196 # ELAIS S1 

197 RA = 9.45 

198 dec = -44. 

199 survey_name = 'DD:ELAISS1' 

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

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

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

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

204 survey_name=survey_name, reward_value=reward_value, 

205 nside=nside, nexp=nexp, detailers=detailers)) 

206 

207 survey_name = 'DD:u,ELAISS1' 

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

209 

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

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

212 nexp=nexp, detailers=detailers)) 

213 

214 # XMM-LSS 

215 survey_name = 'DD:XMM-LSS' 

216 RA = 35.708333 

217 dec = -4-45/60. 

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

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

220 

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

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

223 nside=nside, nexp=nexp, detailers=detailers)) 

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

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

226 

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

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

229 nexp=nexp, detailers=detailers)) 

230 

231 # Extended Chandra Deep Field South 

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

233 RA = 53.125 

234 dec = -28.-6/60. 

235 survey_name = 'DD:ECDFS' 

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

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

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

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

240 survey_name=survey_name, reward_value=reward_value, nside=nside, 

241 nexp=nexp, detailers=detailers)) 

242 

243 survey_name = 'DD:u,ECDFS' 

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

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

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

247 nexp=nexp, detailers=detailers)) 

248 # COSMOS 

249 RA = 150.1 

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

251 survey_name = 'DD:COSMOS' 

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

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

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

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

256 survey_name=survey_name, reward_value=reward_value, nside=nside, 

257 nexp=nexp, detailers=detailers)) 

258 survey_name = 'DD:u,COSMOS' 

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

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

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

262 nexp=nexp, detailers=detailers)) 

263 

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

265 survey_name = 'DD:290' 

266 RA = 349.386443 

267 dec = -63.321004 

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

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

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

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

272 survey_name=survey_name, reward_value=reward_value, nside=nside, 

273 nexp=nexp, detailers=detailers)) 

274 

275 survey_name = 'DD:u,290' 

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

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

278 survey_name=survey_name, reward_value=reward_value, nside=nside, 

279 nexp=nexp, detailers=detailers)) 

280 

281 return surveys