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

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

import numpy as np 

from lsst.sims.featureScheduler.utils import approx_altaz2pa, int_rounded 

import copy 

 

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

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

 

 

class Base_detailer(object): 

""" 

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

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

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

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

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

the exact RA,Dec positions. 

""" 

 

def __init__(self, nside=32): 

""" 

""" 

# Dict to hold all the features we want to track 

self.survey_features = {} 

self.nside = nside 

 

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

""" 

Parameters 

---------- 

observation : np.array 

An array with information about the input observation 

indx : np.array 

The indices of the healpix map that the observation overlaps with 

""" 

for feature in self.survey_features: 

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

 

def __call__(self, observation_list, conditions): 

""" 

Parameters 

---------- 

observation_list : list of observations 

The observations to detail. 

conditions : lsst.sims.featureScheduler.conditions object 

 

Returns 

------- 

List of observations. 

""" 

 

return observation_list 

 

 

class Zero_rot_detailer(Base_detailer): 

""" 

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

Because it can never be written too many times: 

rotSkyPos = rotTelPos - ParallacticAngle 

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

""" 

 

def __call__(self, observation_list, conditions): 

 

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

for obs in observation_list: 

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

conditions.site.longitude_rad, conditions.mjd) 

obs_pa = approx_altaz2pa(alt, az, conditions.site.latitude_rad) 

obs['rotSkyPos'] = obs_pa 

 

return observation_list 

 

 

class Spider_rot_detailer(Base_detailer): 

""" 

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

and columns 

""" 

 

def __call__(self, observation_list, conditions): 

indx = int(conditions.night % 2) 

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

 

for obs in observation_list: 

obs['rotSkyPos'] = np.nan 

obs['rotTelPos'] = rotTelPos 

 

return observation_list 

 

 

class Comcam_90rot_detailer(Base_detailer): 

""" 

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

is closest to rotTelPos of zero. 

""" 

 

def __call__(self, observation_list, conditions): 

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

obs_array =np.concatenate(observation_list) 

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

conditions.site.longitude_rad, conditions.mjd) 

parallactic_angle = approx_altaz2pa(alt, az, conditions.site.latitude_rad) 

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

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

ang_diff = np.abs(parallactic_angle - favored_rotSkyPos) 

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

# can swap 360 and zero if needed? 

final_rotSkyPos = favored_rotSkyPos[min_indxs] 

# Set all the observations to the proper rotSkyPos 

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

obs['rotSkyPos'] = rsp 

 

return observation_list 

 

 

class Close_alt_detailer(Base_detailer): 

""" 

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

 

Parameters 

---------- 

alt_band : float (10) 

The altitude band to try and stay in (degrees) 

""" 

def __init__(self, alt_band=10.): 

super(Close_alt_detailer, self).__init__() 

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

 

def __call__(self, observation_list, conditions): 

obs_array = np.concatenate(observation_list) 

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

conditions.site.longitude_rad, conditions.mjd) 

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

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

if in_band.size == 0: 

in_band = np.arange(alt.size) 

 

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

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

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

indx = in_band[good] 

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

return result 

 

 

class Take_as_pairs_detailer(Base_detailer): 

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

""" 

""" 

super(Take_as_pairs_detailer, self).__init__() 

self.filtername = filtername 

 

def __call__(self, observation_list, conditions): 

paired = copy.deepcopy(observation_list) 

for obs in paired: 

obs['filter'] = self.filtername 

if conditions.current_filter == self.filtername: 

for obs in paired: 

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

for obs in observation_list: 

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

result = paired + observation_list 

else: 

for obs in paired: 

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

for obs in observation_list: 

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

result = observation_list + paired 

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

for i, obs in enumerate(result): 

obs['survey_id'] = i 

return result 

 

 

class Twilight_triple_detailer(Base_detailer): 

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

super(Twilight_triple_detailer, self).__init__() 

self.slew_estimate = slew_estimate 

self.n_repeat = n_repeat 

 

def __call__(self, observation_list, conditions): 

 

obs_array = np.concatenate(observation_list) 

 

# Estimate how much time is left in the twilgiht block 

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

conditions.sun_n12_rising - conditions.mjd]) 

 

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

 

# How long will observations take? 

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

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

cumulative_time = cumulative_slew + cumulative_expt 

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

if np.max(cumulative_time) > potential_times: 

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

if np.size(max_indx) == 0: 

# Very bad magic number fudge 

max_indx = 3 

else: 

max_indx = np.max(max_indx) 

if max_indx == 0: 

max_indx += 1 

observation_list = observation_list[0:max_indx] 

 

# Repeat the observations n times 

out_obs = [] 

for i in range(self.n_repeat): 

out_obs.extend(copy.deepcopy(observation_list)) 

 

return out_obs