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

import numpy as np 

from lsst.sims.featureScheduler import features 

from lsst.sims.featureScheduler import utils 

import healpy as hp 

import matplotlib.pylab as plt 

import warnings 

from lsst.sims.featureScheduler.basis_functions import Base_basis_function 

 

 

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

 

 

class Footprint_basis_function(Base_basis_function): 

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

 

Parameters 

---------- 

filtername : str ('r') 

The filter for this footprint 

footprint : HEALpix np.array 

The desired footprint. Assumed normalized. 

all_footprints_sum : float (None) 

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

normalized properly across all fitlers. 

out_of_bounds_val : float (-10) 

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

another good value to use) 

 

""" 

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

out_of_bounds_val=-10.): 

 

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

self.footprint = footprint 

 

if all_footprints_sum is None: 

# Assume the footprints are similar in weight 

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

else: 

self.all_footprints_sum = all_footprints_sum 

 

self.footprint_sum = np.sum(footprint) 

 

self.survey_features = {} 

# All the observations in all filters 

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

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

 

# should probably actually loop over all the target maps? 

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

self.out_of_bounds_val = out_of_bounds_val 

 

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

 

# Compute how many observations we should have on the sky 

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

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

result[self.out_of_bounds_area] = self.out_of_bounds_val 

return result 

 

 

class Footprint_rolling_basis_function(Base_basis_function): 

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

 

Parameters 

---------- 

footprints : list of np.array 

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

all_footprints_sum : float 

The sum of footprints over all filters. 

all_rolling_sum : float 

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

season_modulo : int (2) 

The modulo to pass to utils.season_calc. 

season_length : float (365.25) 

How long a season should be (days). 

max_season : int (None) 

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

day_offset : np.array (None) 

Offset to pass to utils.season_calc (days). 

 

""" 

 

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

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

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

 

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

# basis function the same as usual for those. 

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

for footprint in footprints[0:-1]: 

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

 

sum_footprints = footprints[0]*0 

for footprint in footprints: 

sum_footprints += footprint 

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

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

 

self.season_modulo = season_modulo 

self.season_length = season_length 

self.max_season = max_season 

self.day_offset = day_offset 

self.footprints = footprints 

 

self.all_footprints_sum = all_footprints_sum 

self.all_rolling_sum = all_rolling_sum 

 

self.survey_features = {} 

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

# All the observations in the given filter 

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

# All the observations in all filters 

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

 

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

# Observation in a season, in filtername 

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

nside=self.nside, 

modulo=season_modulo, 

offset=day_offset, 

max_season=max_season, 

season_length=season_length) 

# Count of all the observations taken in a season 

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

modulo=season_modulo, 

offset=day_offset, 

nside=self.nside, 

max_season=max_season, 

season_length=season_length) 

 

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

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

self.out_of_bounds_val = out_of_bounds_val 

 

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

 

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

result = self.result.copy() 

 

# Compute what season it is at each pixel 

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

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

season_length=self.season_length) 

 

# Compute the constant parts of the footprint like before 

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

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

 

# Now for the rolling sections 

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

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

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]) 

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

 

result[self.out_of_bounds_area] = self.out_of_bounds_val 

return result 

 

 

class Target_map_modulo_basis_function(Base_basis_function): 

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

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

 

Parameters 

---------- 

day_offset : np.array 

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

filtername : (string 'r') 

The name of the filter for this target map. 

nside: int (default_nside) 

The healpix resolution. 

target_maps : list of numpy array (None) 

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

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

norm_factor : float (0.00010519) 

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

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

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

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

that computes norm_factor. 

out_of_bounds_val : float (-10.) 

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

season_modulo : int (2) 

The value to modulate the season by (years). 

max_season : int (None) 

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

 

""" 

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

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

season_length=365.25): 

 

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

 

if norm_factor is None: 

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

self.norm_factor = 0.00010519 

else: 

self.norm_factor = norm_factor 

 

self.survey_features = {} 

# Map of the number of observations in filter 

 

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

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

nside=self.nside, 

modulo=season_modulo, 

offset=day_offset, 

max_season=max_season, 

season_length=season_length) 

# Count of all the observations taken in a season 

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

season_modulo=season_modulo, 

offset=day_offset, 

nside=self.nside, 

max_season=max_season, 

season_length=season_length) 

# Set the final one to be -1 

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

nside=self.nside, 

modulo=season_modulo, 

offset=day_offset, 

max_season=max_season, 

season_length=season_length) 

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

season_modulo=season_modulo, 

offset=day_offset, 

nside=self.nside, 

max_season=max_season, 

season_length=season_length) 

if target_maps is None: 

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

else: 

self.target_maps = target_maps 

# should probably actually loop over all the target maps? 

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

self.out_of_bounds_val = out_of_bounds_val 

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

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

 

# For computing what day each healpix is on 

if day_offset is None: 

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

else: 

self.day_offset = day_offset 

 

self.season_modulo = season_modulo 

self.max_season = max_season 

self.season_length = season_length 

 

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

""" 

Parameters 

---------- 

indx : list (None) 

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

Returns 

------- 

Healpix reward map 

""" 

 

result = self.result.copy() 

if indx is None: 

indx = self.all_indx 

 

# Compute what season it is at each pixel 

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

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

season_length=self.season_length) 

 

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

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

 

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

 

for season in np.unique(seasons): 

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

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

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

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

 

result[indx] = composite_goal_N - composite_nobs[indx] 

result[self.out_of_bounds_area] = self.out_of_bounds_val 

 

return result