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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

from __future__ import absolute_import 

from builtins import object 

import numpy as np 

import healpy as hp 

from lsst.sims.featureScheduler import utils 

from lsst.sims.utils import m5_flat_sed 

 

 

__all__ = ['BaseFeature', 'BaseSurveyFeature', 'N_obs_count', 'N_obs_survey', 

'Last_observation', 'LastSequence_observation', 'LastFilterChange', 

'N_observations', 'Coadded_depth', 'Last_observed', 'N_obs_night', 'Pair_in_night', 

'Rotator_angle'] 

 

 

class BaseFeature(object): 

""" 

Base class for features. 

""" 

def __init__(self, **kwargs): 

# self.feature should be a float, bool, or healpix size numpy array, or numpy masked array 

self.feature = None 

 

def __call__(self): 

return self.feature 

 

 

class BaseSurveyFeature(object): 

""" 

Feature that tracks progreess of the survey. Takes observations and updates self.feature 

""" 

def add_observation(self, observation, indx=None, **kwargs): 

""" 

Parameters 

---------- 

obsevation : dict-like 

Object that contains the information about the observation (ra, dec, filter, mjd, etc) 

indx : ints (None) 

The healpixel indices that the observation overlaps. 

""" 

raise NotImplementedError 

 

 

class N_obs_count(BaseSurveyFeature): 

"""Count the number of observations. Total number, not tracked over sky 

 

Parameters 

---------- 

filtername : str (None) 

The filter to count (if None, all filters counted) 

""" 

def __init__(self, filtername=None, tag=None): 

self.feature = 0 

self.filtername = filtername 

self.tag = tag 

 

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

 

if (self.filtername is None) and (self.tag is None): 

# Track all observations 

self.feature += 1 

elif (self.filtername is not None) and (self.tag is None) and (observation['filter'][0] in self.filtername): 

# Track all observations on a specified filter 

self.feature += 1 

elif (self.filtername is None) and (self.tag is not None) and (observation['tag'][0] in self.tag): 

# Track all observations on a specified tag 

self.feature += 1 

elif ((self.filtername is None) and (self.tag is not None) and 

# Track all observations on a specified filter on a specified tag 

(observation['filter'][0] in self.filtername) and (observation['tag'][0] in self.tag)): 

self.feature += 1 

 

 

class N_obs_survey(BaseSurveyFeature): 

"""Count the number of observations. 

 

Parameters 

---------- 

note : str (None) 

Only count observations that have str in their note field 

""" 

def __init__(self, note=None): 

self.feature = 0 

self.note = note 

 

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

# Track all observations 

if self.note is None: 

self.feature += 1 

else: 

if self.note in observation['note']: 

self.feature += 1 

 

 

class Last_observation(BaseSurveyFeature): 

"""Track the last observation. Useful if you want to see when the 

last time a survey took an observation. 

 

Parameters 

---------- 

survey_name : str (None) 

Only records if the survey name matches (or survey_name set to None) 

""" 

def __init__(self, survey_name=None): 

self.survey_name = survey_name 

# Start out with an empty observation 

self.feature = utils.empty_observation() 

 

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

if self.survey_name is not None: 

if self.survey_name in observation['note']: 

self.feature = observation 

else: 

self.feature = observation 

 

 

class LastSequence_observation(BaseSurveyFeature): 

"""When was the last observation 

""" 

def __init__(self, sequence_ids=''): 

self.sequence_ids = sequence_ids # The ids of all sequence observations... 

# Start out with an empty observation 

self.feature = utils.empty_observation() 

 

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

if observation['survey_id'] in self.sequence_ids: 

self.feature = observation 

 

 

class LastFilterChange(BaseSurveyFeature): 

"""Record when the filter last changed. 

""" 

def __init__(self): 

self.feature = {'mjd': 0., 

'previous_filter': None, 

'current_filter': None} 

 

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

if self.feature['current_filter'] is None: 

self.feature['mjd'] = observation['mjd'][0] 

self.feature['previous_filter'] = None 

self.feature['current_filter'] = observation['filter'][0] 

elif observation['filter'][0] != self.feature['current_filter']: 

self.feature['mjd'] = observation['mjd'][0] 

self.feature['previous_filter'] = self.feature['current_filter'] 

self.feature['current_filter'] = observation['filter'][0] 

 

 

class N_observations(BaseSurveyFeature): 

""" 

Track the number of observations that have been made across the sky. 

 

Parameters 

---------- 

filtername : str ('r') 

String or list that has all the filters that can count. 

nside : int (32) 

The nside of the healpixel map to use 

mask_indx : list of ints (None) 

List of healpixel indices to mask and interpolate over 

 

""" 

def __init__(self, filtername=None, nside=None, mask_indx=None): 

if nside is None: 

nside = utils.set_default_nside() 

 

self.feature = np.zeros(hp.nside2npix(nside), dtype=float) 

self.filtername = filtername 

self.mask_indx = mask_indx 

 

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

""" 

Parameters 

---------- 

indx : ints 

The indices of the healpixel map that have been observed by observation 

""" 

if self.filtername is None or observation['filter'][0] in self.filtername: 

self.feature[indx] += 1 

 

if self.mask_indx is not None: 

overlap = np.intersect1d(indx, self.mask_indx) 

if overlap.size > 0: 

# interpolate over those pixels that are DD fields. 

# XXX. Do I need to kdtree this? Maybe make a dict on init 

# to lookup the N closest non-masked pixels, then do weighted average. 

pass 

 

 

class Coadded_depth(BaseSurveyFeature): 

""" 

Track the co-added depth that has been reached accross the sky 

""" 

def __init__(self, filtername='r', nside=None): 

if nside is None: 

nside = utils.set_default_nside() 

self.filtername = filtername 

# Starting at limiting mag of zero should be fine. 

self.feature = np.zeros(hp.nside2npix(nside), dtype=float) 

 

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

 

if observation['filter'][0] == self.filtername: 

m5 = m5_flat_sed(observation['filter'][0], observation['skybrightness'][0], 

observation['FWHMeff'][0], observation['exptime'][0], 

observation['airmass'][0]) 

self.feature[indx] = 1.25 * np.log10(10.**(0.8*self.feature[indx]) + 10.**(0.8*m5)) 

 

 

class Last_observed(BaseSurveyFeature): 

""" 

Track when a pixel was last observed. Assumes observations are added in chronological 

order. 

""" 

def __init__(self, filtername='r', nside=None): 

if nside is None: 

nside = utils.set_default_nside() 

 

self.filtername = filtername 

self.feature = np.zeros(hp.nside2npix(nside), dtype=float) 

 

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

if self.filtername is None: 

self.feature[indx] = observation['mjd'] 

elif observation['filter'][0] in self.filtername: 

self.feature[indx] = observation['mjd'] 

 

 

class N_obs_night(BaseSurveyFeature): 

""" 

Track how many times something has been observed in a night 

(Note, even if there are two, it might not be a good pair.) 

 

Parameters 

---------- 

filtername : string ('r') 

Filter to track. 

nside : int (32) 

Scale of the healpix map 

 

""" 

def __init__(self, filtername='r', nside=None): 

if nside is None: 

nside = utils.set_default_nside() 

 

self.filtername = filtername 

self.feature = np.zeros(hp.nside2npix(nside), dtype=int) 

self.night = None 

 

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

if observation['night'][0] != self.night: 

self.feature *= 0 

self.night = observation['night'][0] 

if observation['filter'][0] in self.filtername: 

self.feature[indx] += 1 

 

 

class Pair_in_night(BaseSurveyFeature): 

""" 

Track how many pairs have been observed within a night 

 

Parameters 

---------- 

gap_min : float (25.) 

The minimum time gap to consider a successful pair in minutes 

gap_max : float (45.) 

The maximum time gap to consider a successful pair (minutes) 

""" 

def __init__(self, filtername='r', nside=None, gap_min=25., gap_max=45.): 

if nside is None: 

nside = utils.set_default_nside() 

 

self.filtername = filtername 

self.feature = np.zeros(hp.nside2npix(nside), dtype=float) 

self.indx = np.arange(self.feature.size) 

self.last_observed = Last_observed(filtername=filtername) 

self.gap_min = gap_min / (24.*60) # Days 

self.gap_max = gap_max / (24.*60) # Days 

self.night = 0 

# Need to keep a full record of times and healpixels observed in a night. 

self.mjd_log = [] 

self.hpid_log = [] 

 

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

if observation['filter'][0] in self.filtername: 

if indx is None: 

indx = self.indx 

# Clear values if on a new night 

if self.night != observation['night']: 

self.feature *= 0. 

self.night = observation['night'] 

self.mjd_log = [] 

self.hpid_log = [] 

 

# record the mjds and healpixels that were observed 

self.mjd_log.extend([np.max(observation['mjd'])]*np.size(indx)) 

self.hpid_log.extend(list(indx)) 

 

# Look for the mjds that could possibly pair with observation 

tmin = observation['mjd'] - self.gap_max 

tmax = observation['mjd'] - self.gap_min 

mjd_log = np.array(self.mjd_log) 

left = np.searchsorted(mjd_log, tmin) 

right = np.searchsorted(mjd_log, tmax, side='right') 

# Now check if any of the healpixels taken in the time gap 

# match the healpixels of the observation. 

matches = np.in1d(indx, self.hpid_log[int(left):int(right)]) 

# XXX--should think if this is the correct (fastest) order to check things in. 

self.feature[indx[matches]] += 1 

 

 

class Rotator_angle(BaseSurveyFeature): 

""" 

Track what rotation angles things are observed with. 

XXX-under construction 

""" 

def __init__(self, filtername='r', binsize=10., nside=None): 

""" 

 

""" 

if nside is None: 

nside = utils.set_default_nside() 

 

self.filtername = filtername 

# Actually keep a histogram at each healpixel 

self.feature = np.zeros((hp.nside2npix(nside), 360./binsize), dtype=float) 

self.bins = np.arange(0, 360+binsize, binsize) 

 

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

if observation['filter'][0] == self.filtername: 

# I think this is how to broadcast things properly. 

self.feature[indx, :] += np.histogram(observation.rotSkyPos, bins=self.bins)[0]