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

import numpy as np 

from lsst.sims.featureScheduler import features 

import matplotlib.pylab as plt 

from lsst.sims.featureScheduler.basis_functions import Base_basis_function 

 

 

__all__ = ['Filter_loaded_basis_function', 'Time_to_twilight_basis_function', 

'Not_twilight_basis_function', 'Force_delay_basis_function', 

'Hour_Angle_limit_basis_function', 'Moon_down_basis_function', 

'Fraction_of_obs_basis_function', 'Clouded_out_basis_function', 

'Rising_more_basis_function', 'Soft_delay_basis_function', 

'Look_ahead_ddf_basis_function'] 

 

 

class Filter_loaded_basis_function(Base_basis_function): 

"""Check that the filter(s) needed are loaded 

 

Parameters 

---------- 

filternames : str or list of str 

The filternames that need to be mounted to execute. 

""" 

def __init__(self, filternames='r'): 

super(Filter_loaded_basis_function, self).__init__() 

if type(filternames) is not list: 

filternames = [filternames] 

self.filternames = filternames 

 

def check_feasibility(self, conditions): 

 

for filtername in self.filternames: 

result = filtername in conditions.mounted_filters 

if result is False: 

return result 

return result 

 

 

class Time_to_twilight_basis_function(Base_basis_function): 

"""Make sure there is enough time before twilight. Useful 

if you want to check before starting a long sequence of observations. 

 

Parameters 

---------- 

time_needed : float (30.) 

The time needed to run a survey (mintues). 

alt_limit : int (18) 

The sun altitude limit to use. Must be 12 or 18 

""" 

def __init__(self, time_needed=30., alt_limit=18): 

super(Time_to_twilight_basis_function, self).__init__() 

self.time_needed = time_needed/60./24. # To days 

self.alt_limit = str(alt_limit) 

 

def check_feasibility(self, conditions): 

available_time = getattr(conditions, 'sun_n' + self.alt_limit + '_rising') - conditions.mjd 

result = available_time > self.time_needed 

return result 

 

 

class Not_twilight_basis_function(Base_basis_function): 

def __init__(self, sun_alt_limit=-18): 

""" 

# Should be -18 or -12 

""" 

self.sun_alt_limit = str(int(sun_alt_limit)).replace('-', 'n') 

super(Not_twilight_basis_function, self).__init__() 

 

def check_feasibility(self, conditions): 

result = True 

if conditions.mjd < getattr(conditions, 'sun_'+self.sun_alt_limit+'_setting'): 

result = False 

if conditions.mjd > getattr(conditions, 'sun_'+self.sun_alt_limit+'_rising'): 

result = False 

return result 

 

 

class Force_delay_basis_function(Base_basis_function): 

"""Keep a survey from executing to rapidly. 

 

Parameters 

---------- 

days_delay : float (2) 

The number of days to force a gap on. 

""" 

def __init__(self, days_delay=2., survey_name=None): 

super(Force_delay_basis_function, self).__init__() 

self.days_delay = days_delay 

self.survey_name = survey_name 

self.survey_features['last_obs_self'] = features.Last_observation(survey_name=self.survey_name) 

 

def check_feasibility(self, conditions): 

result = True 

if conditions.mjd - self.survey_features['last_obs_self'].feature['mjd'] < self.days_delay: 

result = False 

return result 

 

 

class Soft_delay_basis_function(Base_basis_function): 

"""Like Force_delay, but go ahead and let things catch up if they fall far behind. 

 

Parameters 

---------- 

 

""" 

def __init__(self, fractions=[0.000, 0.009, 0.017], delays=[0., 0.5, 1.5], survey_name=None): 

if len(fractions) != len(delays): 

raise ValueError('fractions and delays must be same length') 

super(Soft_delay_basis_function, self).__init__() 

self.delays = delays 

self.survey_name = survey_name 

self.survey_features['last_obs_self'] = features.Last_observation(survey_name=self.survey_name) 

self.fractions = fractions 

self.survey_features['Ntot'] = features.N_obs_survey() 

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

 

def check_feasibility(self, conditions): 

result = True 

current_ratio = self.survey_features['N_survey'].feature / self.survey_features['Ntot'].feature 

indx = np.searchsorted(self.fractions, current_ratio) 

if indx == len(self.fractions): 

indx -= 1 

delay = self.delays[indx] 

if conditions.mjd - self.survey_features['last_obs_self'].feature['mjd'] < delay: 

result = False 

return result 

 

 

class Hour_Angle_limit_basis_function(Base_basis_function): 

"""Only execute a survey in limited hour angle ranges. Useful for 

limiting Deep Drilling Fields. 

 

Parameters 

---------- 

RA : float (0.) 

RA of the target (degrees). 

ha_limits : list of lists 

limits for what hour angles are acceptable (hours). e.g., 

to give 4 hour window around RA=0, ha_limits=[[22,24], [0,2]] 

""" 

def __init__(self, RA=0., ha_limits=None): 

super(Hour_Angle_limit_basis_function, self).__init__() 

self.ra_hours = RA/360.*24. 

self.HA_limits = np.array(ha_limits) 

 

def check_feasibility(self, conditions): 

target_HA = (conditions.lmst - self.ra_hours) % 24 

# Are we in any of the possible windows 

result = False 

for limit in self.HA_limits: 

lres = limit[0] <= target_HA < limit[1] 

result = result or lres 

 

return result 

 

 

class Moon_down_basis_function(Base_basis_function): 

"""Demand the moon is down """ 

def check_feasibility(self, conditions): 

result = True 

if conditions.moonAlt > 0: 

result = False 

return result 

 

 

class Fraction_of_obs_basis_function(Base_basis_function): 

"""Limit the fraction of all observations that can be labled a certain 

survey name. Useful for keeping DDFs from exceeding a given fraction of the 

total survey. 

 

Parameters 

---------- 

frac_total : float 

The fraction of total observations that can be of this survey 

survey_name : str 

The name of the survey 

""" 

def __init__(self, frac_total, survey_name=''): 

super(Fraction_of_obs_basis_function, self).__init__() 

self.survey_name = survey_name 

self.frac_total = frac_total 

self.survey_features['Ntot'] = features.N_obs_survey() 

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

 

def check_feasibility(self, conditions): 

# If nothing has been observed, fine to go 

result = True 

if self.survey_features['Ntot'].feature == 0: 

return result 

ratio = self.survey_features['N_survey'].feature / self.survey_features['Ntot'].feature 

if ratio > self.frac_total: 

result = False 

return result 

 

 

class Look_ahead_ddf_basis_function(Base_basis_function): 

"""Look into the future to decide if it's a good time to observe or block. 

 

Parameters 

---------- 

frac_total : float 

The fraction of total observations that can be of this survey 

aggressive_fraction : float 

If the fraction of observations drops below ths value, be more aggressive in scheduling. 

e.g., do not wait for conditions to improve, execute as soon as possible. 

time_needed : float (30.) 

Estimate of the amount of time needed to execute DDF sequence (minutes). 

RA : float (0.) 

The RA of the DDF 

ha_limits : list of lists (None) 

limits for what hour angles are acceptable (hours). e.g., 

to give 4 hour window around HA=0, ha_limits=[[22,24], [0,2]] 

survey_name : str ('') 

The name of the survey 

time_jump : float (44.) 

The amount of time to assume will jump ahead if another survey executes (minutes) 

sun_alt_limit : float (-18.) 

The limit to assume twilight starts (degrees) 

""" 

def __init__(self, frac_total, aggressive_fraction, time_needed=30., RA=0., 

ha_limits=None, survey_name='', time_jump=44., sun_alt_limit=-18.): 

super(Look_ahead_ddf_basis_function, self).__init__() 

if aggressive_fraction > frac_total: 

raise ValueError('aggressive_fraction should be less than frac_total') 

self.survey_name = survey_name 

self.frac_total = frac_total 

self.ra_hours = RA/360.*24. 

self.HA_limits = np.array(ha_limits) 

self.sun_alt_limit = str(int(sun_alt_limit)).replace('-', 'n') 

self.time_jump = time_jump / 60. # To hours 

self.time_needed = time_needed / 60. # To hours 

self.aggressive_fraction = aggressive_fraction 

self.survey_features['Ntot'] = features.N_obs_survey() 

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

 

def check_feasibility(self, conditions): 

result = True 

target_HA = (conditions.lmst - self.ra_hours) % 24 

ratio = self.survey_features['N_survey'].feature / self.survey_features['Ntot'].feature 

available_time = getattr(conditions, 'sun_' + self.sun_alt_limit + '_rising') - conditions.mjd 

# If it's more that self.time_jump to hour angle zero 

# See if there will be enough time to twilight in the future 

if (target_HA > 12) & (target_HA < 24.-self.time_jump): 

if available_time > (self.time_needed + self.time_jump): 

result = False 

# If the survey has fallen far behind, be agressive and observe anytime it's up. 

if ratio < self.aggressive_fraction: 

result = True 

return result 

 

 

class Clouded_out_basis_function(Base_basis_function): 

def __init__(self, cloud_limit=0.7): 

super(Clouded_out_basis_function, self).__init__() 

self.cloud_limit = cloud_limit 

 

def check_feasibility(self, conditions): 

result = True 

if conditions.bulk_cloud > self.cloud_limit: 

result = False 

return result 

 

 

class Rising_more_basis_function(Base_basis_function): 

"""Say a spot is not available if it will rise substatially before twilight. 

 

Parameters 

---------- 

RA : float 

The RA of the point in the sky (degrees) 

pad : float 

When to start observations if there's plenty of time before twilight (minutes) 

""" 

def __init__(self, RA, pad=30.): 

super(Rising_more_basis_function, self).__init__() 

self.RA_hours = RA * 24 / 360. 

self.pad = pad/60. # To hours 

 

def check_feasibility(self, conditions): 

result = True 

hour_angle = conditions.lmst - self.RA_hours 

# If it's rising, and twilight is well beyond when it crosses the meridian 

time_to_twi = (conditions.sun_n18_rising - conditions.mjd)*24. 

if (hour_angle < -self.pad) & (np.abs(hour_angle) < (time_to_twi - self.pad)): 

result = False 

return result 

 

 

## XXX--TODO: Can include checks to see if downtime is coming, clouds are coming, moon rising, or surveys in a higher tier  

# Have observations they want to execute soon.