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 import features 

3from lsst.sims.featureScheduler import utils 

4import healpy as hp 

5import matplotlib.pylab as plt 

6import warnings 

7from lsst.sims.featureScheduler.basis_functions import Base_basis_function 

8 

9 

10__all__ = ["Target_map_modulo_basis_function", "Footprint_basis_function", "Footprint_rolling_basis_function"] 

11 

12 

13class Footprint_basis_function(Base_basis_function): 

14 """Basis function that tries to maintain a uniformly covered footprint 

15 

16 Parameters 

17 ---------- 

18 filtername : str ('r') 

19 The filter for this footprint 

20 footprint : HEALpix np.array 

21 The desired footprint. Assumed normalized. 

22 all_footprints_sum : float (None) 

23 If using multiple filters, the sum of all the footprints. Needed to make sure basis functions are 

24 normalized properly across all fitlers. 

25 out_of_bounds_val : float (-10) 

26 The value to set the basis function for regions that are not in the footprint (default -10, np.nan is 

27 another good value to use) 

28 

29 """ 

30 def __init__(self, filtername='r', nside=None, footprint=None, all_footprints_sum=None, 

31 out_of_bounds_val=-10.): 

32 

33 super(Footprint_basis_function, self).__init__(nside=nside, filtername=filtername) 

34 self.footprint = footprint 

35 

36 if all_footprints_sum is None: 

37 # Assume the footprints are similar in weight 

38 self.all_footprints_sum = np.sum(footprint)*6 

39 else: 

40 self.all_footprints_sum = all_footprints_sum 

41 

42 self.footprint_sum = np.sum(footprint) 

43 

44 self.survey_features = {} 

45 # All the observations in all filters 

46 self.survey_features['N_obs_all'] = features.N_observations(nside=nside, filtername=None) 

47 self.survey_features['N_obs'] = features.N_observations(nside=nside, filtername=filtername) 

48 

49 # should probably actually loop over all the target maps? 

50 self.out_of_bounds_area = np.where(footprint <= 0)[0] 

51 self.out_of_bounds_val = out_of_bounds_val 

52 

53 def _calc_value(self, conditions, indx=None): 

54 

55 # Compute how many observations we should have on the sky 

56 desired = self.footprint / self.all_footprints_sum * np.sum(self.survey_features['N_obs_all'].feature) 

57 result = desired - self.survey_features['N_obs'].feature 

58 result[self.out_of_bounds_area] = self.out_of_bounds_val 

59 return result 

60 

61 

62class Footprint_rolling_basis_function(Base_basis_function): 

63 """Let's get the rolling really right. 

64 

65 Parameters 

66 ---------- 

67 footprints : list of np.array 

68 List of HEALpix arrays. The footprints should all have matching sums and have the same nside. 

69 all_footprints_sum : float 

70 The sum of footprints over all filters. 

71 all_rolling_sum : float 

72 The sum (over all filters) of the region of the maps that changs. 

73 season_modulo : int (2) 

74 The modulo to pass to utils.season_calc. 

75 season_length : float (365.25) 

76 How long a season should be (days). 

77 max_season : int (None) 

78 If set, the season calc will return -1 for values greater than max_season 

79 day_offset : np.array (None) 

80 Offset to pass to utils.season_calc (days). 

81 

82 """ 

83 

84 def __init__(self, filtername='r', nside=None, footprints=None, all_footprints_sum=None, all_rolling_sum=None, out_of_bounds_val=-10, 

85 season_modulo=2, season_length=365.25, max_season=None, day_offset=None): 

86 super(Footprint_rolling_basis_function, self).__init__(nside=nside, filtername=filtername) 

87 

88 # OK, going to find the parts of the map that are the same everywhere, and compute the 

89 # basis function the same as usual for those. 

90 same_footprint = np.ones(footprints[0].size, dtype=bool) 

91 for footprint in footprints[0:-1]: 

92 same_footprint = same_footprint & (footprint == footprints[-1]) 

93 

94 sum_footprints = footprints[0]*0 

95 for footprint in footprints: 

96 sum_footprints += footprint 

97 self.constant_footprint_indx = np.where((same_footprint == True) & (sum_footprints > 0))[0] 

98 self.rolling_footprint_indx = np.where((same_footprint == False) & (sum_footprints > 0))[0] 

99 

100 self.season_modulo = season_modulo 

101 self.season_length = season_length 

102 self.max_season = max_season 

103 self.day_offset = day_offset 

104 self.footprints = footprints 

105 

106 self.all_footprints_sum = all_footprints_sum 

107 self.all_rolling_sum = all_rolling_sum 

108 

109 self.survey_features = {} 

110 # Set a season for -1 (for before rolling or after max_season) 

111 # All the observations in the given filter 

112 self.survey_features['N_obs_%i' % -1] = features.N_observations(nside=nside, filtername=filtername) 

113 # All the observations in all filters 

114 self.survey_features['N_obs_all_%i' % -1] = features.N_observations(nside=nside, filtername=None) 

115 

116 for i, temp in enumerate(footprints[0:-1]): 

117 # Observation in a season, in filtername 

118 self.survey_features['N_obs_%i' % i] = features.N_observations_season(i, filtername=filtername, 

119 nside=self.nside, 

120 modulo=season_modulo, 

121 offset=day_offset, 

122 max_season=max_season, 

123 season_length=season_length) 

124 # Count of all the observations taken in a season 

125 self.survey_features['N_obs_all_%i' % i] = features.N_observations_season(i, filtername=None, 

126 modulo=season_modulo, 

127 offset=day_offset, 

128 nside=self.nside, 

129 max_season=max_season, 

130 season_length=season_length) 

131 

132 # Now I need to track the observations taken in each season. 

133 self.out_of_bounds_area = np.where(footprint <= 0)[0] 

134 self.out_of_bounds_val = out_of_bounds_val 

135 

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

137 

138 def _calc_value(self, conditions, indx=None): 

139 result = self.result.copy() 

140 

141 # Compute what season it is at each pixel 

142 seasons = utils.season_calc(conditions.night, offset=self.day_offset, 

143 modulo=self.season_modulo, max_season=self.max_season, 

144 season_length=self.season_length) 

145 

146 # Compute the constant parts of the footprint like before 

147 desired = self.footprints[-1] / self.all_footprints_sum * np.sum(self.survey_features['N_obs_all_-1'].feature) 

148 result[self.constant_footprint_indx] = desired[self.constant_footprint_indx] - self.survey_features['N_obs_-1'].feature[self.constant_footprint_indx] 

149 

150 # Now for the rolling sections 

151 for season in np.unique(seasons[self.rolling_footprint_indx]): 

152 season_indx = np.where(seasons[self.rolling_footprint_indx] == season)[0] 

153 desired = self.footprints[season][self.rolling_footprint_indx][season_indx] / self.all_rolling_sum * np.sum(self.survey_features['N_obs_all_%i' % season].feature[self.rolling_footprint_indx]) 

154 result[self.rolling_footprint_indx[season_indx]] = desired - self.survey_features['N_obs_%i' % season].feature[self.rolling_footprint_indx][season_indx] 

155 

156 result[self.out_of_bounds_area] = self.out_of_bounds_val 

157 return result 

158 

159 

160class Target_map_modulo_basis_function(Base_basis_function): 

161 """Basis function that tracks number of observations and tries to match a specified spatial distribution 

162 can enter multiple maps that will be used at different times in the survey 

163 

164 Parameters 

165 ---------- 

166 day_offset : np.array 

167 Healpix map that has the offset to be applied to each pixel when computing what season it is on. 

168 filtername : (string 'r') 

169 The name of the filter for this target map. 

170 nside: int (default_nside) 

171 The healpix resolution. 

172 target_maps : list of numpy array (None) 

173 healpix maps showing the ratio of observations desired for all points on the sky. Last map will be used 

174 for season -1. Probably shouldn't support going to season less than -1. 

175 norm_factor : float (0.00010519) 

176 for converting target map to number of observations. Should be the area of the camera 

177 divided by the area of a healpixel divided by the sum of all your goal maps. Default 

178 value assumes LSST foV has 1.75 degree radius and the standard goal maps. If using 

179 mulitple filters, see lsst.sims.featureScheduler.utils.calc_norm_factor for a utility 

180 that computes norm_factor. 

181 out_of_bounds_val : float (-10.) 

182 Reward value to give regions where there are no observations requested (unitless). 

183 season_modulo : int (2) 

184 The value to modulate the season by (years). 

185 max_season : int (None) 

186 For seasons higher than this value (pre-modulo), the final target map is used. 

187 

188 """ 

189 def __init__(self, day_offset=None, filtername='r', nside=None, target_maps=None, 

190 norm_factor=None, out_of_bounds_val=-10., season_modulo=2, max_season=None, 

191 season_length=365.25): 

192 

193 super(Target_map_modulo_basis_function, self).__init__(nside=nside, filtername=filtername) 

194 

195 if norm_factor is None: 

196 warnings.warn('No norm_factor set, use utils.calc_norm_factor if using multiple filters.') 

197 self.norm_factor = 0.00010519 

198 else: 

199 self.norm_factor = norm_factor 

200 

201 self.survey_features = {} 

202 # Map of the number of observations in filter 

203 

204 for i, temp in enumerate(target_maps[0:-1]): 

205 self.survey_features['N_obs_%i' % i] = features.N_observations_season(i, filtername=filtername, 

206 nside=self.nside, 

207 modulo=season_modulo, 

208 offset=day_offset, 

209 max_season=max_season, 

210 season_length=season_length) 

211 # Count of all the observations taken in a season 

212 self.survey_features['N_obs_count_all_%i' % i] = features.N_obs_count_season(i, filtername=None, 

213 season_modulo=season_modulo, 

214 offset=day_offset, 

215 nside=self.nside, 

216 max_season=max_season, 

217 season_length=season_length) 

218 # Set the final one to be -1 

219 self.survey_features['N_obs_%i' % -1] = features.N_observations_season(-1, filtername=filtername, 

220 nside=self.nside, 

221 modulo=season_modulo, 

222 offset=day_offset, 

223 max_season=max_season, 

224 season_length=season_length) 

225 self.survey_features['N_obs_count_all_%i' % -1] = features.N_obs_count_season(-1, filtername=None, 

226 season_modulo=season_modulo, 

227 offset=day_offset, 

228 nside=self.nside, 

229 max_season=max_season, 

230 season_length=season_length) 

231 if target_maps is None: 

232 self.target_maps = utils.generate_goal_map(filtername=filtername, nside=self.nside) 

233 else: 

234 self.target_maps = target_maps 

235 # should probably actually loop over all the target maps? 

236 self.out_of_bounds_area = np.where(self.target_maps[0] == 0)[0] 

237 self.out_of_bounds_val = out_of_bounds_val 

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

239 self.all_indx = np.arange(self.result.size) 

240 

241 # For computing what day each healpix is on 

242 if day_offset is None: 

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

244 else: 

245 self.day_offset = day_offset 

246 

247 self.season_modulo = season_modulo 

248 self.max_season = max_season 

249 self.season_length = season_length 

250 

251 def _calc_value(self, conditions, indx=None): 

252 """ 

253 Parameters 

254 ---------- 

255 indx : list (None) 

256 Index values to compute, if None, full map is computed 

257 Returns 

258 ------- 

259 Healpix reward map 

260 """ 

261 

262 result = self.result.copy() 

263 if indx is None: 

264 indx = self.all_indx 

265 

266 # Compute what season it is at each pixel 

267 seasons = utils.season_calc(conditions.night, offset=self.day_offset, 

268 modulo=self.season_modulo, max_season=self.max_season, 

269 season_length=self.season_length) 

270 

271 composite_target = self.result.copy()[indx] 

272 composite_nobs = self.result.copy()[indx] 

273 

274 composite_goal_N = self.result.copy()[indx] 

275 

276 for season in np.unique(seasons): 

277 season_indx = np.where(seasons == season)[0] 

278 composite_target[season_indx] = self.target_maps[season][season_indx] 

279 composite_nobs[season_indx] = self.survey_features['N_obs_%i' % season].feature[season_indx] 

280 composite_goal_N[season_indx] = composite_target[season_indx] * self.survey_features['N_obs_count_all_%i' % season].feature * self.norm_factor 

281 

282 result[indx] = composite_goal_N - composite_nobs[indx] 

283 result[self.out_of_bounds_area] = self.out_of_bounds_val 

284 

285 return result