Coverage for python/lsst/sims/featureScheduler/surveys/dd_surveys.py : 10%

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.surveys import BaseSurvey
3import copy
4import lsst.sims.featureScheduler.basis_functions as basis_functions
5from lsst.sims.featureScheduler.utils import empty_observation
6from lsst.sims.featureScheduler import features
7import logging
8import random
11__all__ = ['Deep_drilling_survey', 'generate_dd_surveys', 'dd_bfs', 'dd_u_bfs']
13log = logging.getLogger(__name__)
16class Deep_drilling_survey(BaseSurvey):
17 """A survey class for running deep drilling fields.
19 Parameters
20 ----------
21 basis_functions : list of lsst.sims.featureScheduler.basis_function objects
22 These should be feasibility basis functions.
23 RA : float
24 The RA of the field (degrees)
25 dec : float
26 The dec of the field to observe (degrees)
27 sequence : list of observation objects or str (rgizy)
28 The sequence of observations to take. Can be a string of list of obs objects.
29 nvis : list of ints
30 The number of visits in each filter. Should be same length as sequence.
31 survey_name : str (DD)
32 The name to give this survey so it can be tracked
33 reward_value : float (101.)
34 The reward value to report if it is able to start (unitless).
35 readtime : float (2.)
36 Readout time for computing approximate time of observing the sequence. (seconds)
37 filter_match_shuffle : bool (True)
38 If True, switch up the order filters are executed in (first sequence will be currently
39 loaded filter if possible)
40 flush_pad : float (30.)
41 How long to hold observations in the queue after they were expected to be completed (minutes).
42 """
44 def __init__(self, basis_functions, RA, dec, sequence='rgizy',
45 nvis=[20, 10, 20, 26, 20],
46 exptime=30., nexp=2, ignore_obs=None, survey_name='DD',
47 reward_value=None, readtime=2., filter_change_time=120.,
48 nside=None, filter_match_shuffle=True, flush_pad=30., seed=42, detailers=None):
49 super(Deep_drilling_survey, self).__init__(nside=nside, basis_functions=basis_functions,
50 detailers=detailers, ignore_obs=ignore_obs)
51 random.seed(a=seed)
53 self.ra = np.radians(RA)
54 self.ra_hours = RA/360.*24.
55 self.dec = np.radians(dec)
56 self.survey_name = survey_name
57 self.reward_value = reward_value
58 self.flush_pad = flush_pad/60./24. # To days
59 self.filter_sequence = []
60 if type(sequence) == str:
61 self.observations = []
62 for num, filtername in zip(nvis, sequence):
63 for j in range(num):
64 obs = empty_observation()
65 obs['filter'] = filtername
66 obs['exptime'] = exptime
67 obs['RA'] = self.ra
68 obs['dec'] = self.dec
69 obs['nexp'] = nexp
70 obs['note'] = survey_name
71 self.observations.append(obs)
72 self.filter_sequence.append(filtername)
73 else:
74 self.observations = sequence
75 self.filter_sequence = [obs['filter'] for obs in sequence]
77 # Make an estimate of how long a seqeunce will take. Assumes no major rotational or spatial
78 # dithering slowing things down.
79 self.approx_time = np.sum([o['exptime']+readtime*o['nexp'] for o in self.observations])/3600./24. \
80 + filter_change_time*len(sequence)/3600./24. # to days
81 self.filter_match_shuffle = filter_match_shuffle
82 self.filter_indices = {}
83 self.filter_sequence = np.array(self.filter_sequence)
84 for filtername in np.unique(self.filter_sequence):
85 self.filter_indices[filtername] = np.where(self.filter_sequence == filtername)[0]
87 if self.reward_value is None:
88 self.extra_features['Ntot'] = features.N_obs_survey()
89 self.extra_features['N_survey'] = features.N_obs_survey(note=self.survey_name)
91 def check_continue(self, observation, conditions):
92 # feasibility basis functions?
93 '''
94 This method enables external calls to check if a given observations that belongs to this survey is
95 feasible or not. This is called once a sequence has started to make sure it can continue.
97 XXX--TODO: Need to decide if we want to develope check_continue, or instead hold the
98 sequence in the survey, and be able to check it that way.
99 '''
101 result = True
103 return result
105 def calc_reward_function(self, conditions):
106 result = -np.inf
107 if self._check_feasibility(conditions):
108 if self.reward_value is not None:
109 result = self.reward_value
110 else:
111 # XXX This might backfire if we want to have DDFs with different fractions of the
112 # survey time. Then might need to define a goal fraction, and have the reward be the
113 # number of observations behind that target fraction.
114 result = self.extra_features['Ntot'].feature / (self.extra_features['N_survey'].feature+1)
115 return result
117 def generate_observations_rough(self, conditions):
118 result = []
119 if self._check_feasibility(conditions):
120 result = copy.deepcopy(self.observations)
122 # Toss any filters that are not currently loaded
123 result = [obs for obs in result if obs['filter'] in conditions.mounted_filters]
125 if self.filter_match_shuffle:
126 filters_remaining = list(self.filter_indices.keys())
127 random.shuffle(filters_remaining)
128 # If we want to observe the currrent filter, put it first
129 if conditions.current_filter in filters_remaining:
130 filters_remaining.insert(0, filters_remaining.pop(filters_remaining.index(conditions.current_filter)))
131 final_result = []
132 for filtername in filters_remaining:
133 final_result.extend(result[np.min(self.filter_indices[filtername]):np.max(self.filter_indices[filtername])+1])
134 result = final_result
135 # Let's set the mjd to flush the queue by
136 for i, obs in enumerate(result):
137 result[i]['flush_by_mjd'] = conditions.mjd + self.approx_time + self.flush_pad
138 return result
141def dd_bfs(RA, dec, survey_name, ha_limits, frac_total=0.0185, aggressive_frac=0.011):
142 """
143 Convienence function to generate all the feasibility basis functions
144 """
145 sun_alt_limit = -18.
146 time_needed = 62.
147 fractions = [0.00, aggressive_frac, frac_total]
148 bfs = []
149 bfs.append(basis_functions.Filter_loaded_basis_function(filternames=['r', 'g', 'i', 'z', 'y']))
150 bfs.append(basis_functions.Not_twilight_basis_function(sun_alt_limit=sun_alt_limit))
151 bfs.append(basis_functions.Time_to_twilight_basis_function(time_needed=time_needed))
152 bfs.append(basis_functions.Hour_Angle_limit_basis_function(RA=RA, ha_limits=ha_limits))
153 bfs.append(basis_functions.Fraction_of_obs_basis_function(frac_total=frac_total, survey_name=survey_name))
154 bfs.append(basis_functions.Look_ahead_ddf_basis_function(frac_total, aggressive_frac,
155 sun_alt_limit=sun_alt_limit, time_needed=time_needed,
156 RA=RA, survey_name=survey_name,
157 ha_limits=ha_limits))
158 bfs.append(basis_functions.Soft_delay_basis_function(fractions=fractions, delays=[0., 0.5, 1.5],
159 survey_name=survey_name))
161 return bfs
164def dd_u_bfs(RA, dec, survey_name, ha_limits, frac_total=0.0019, aggressive_frac=0.0014):
165 """Convienence function to generate all the feasibility basis functions for u-band DDFs
166 """
167 bfs = []
168 sun_alt_limit = -18.
169 time_needed = 6.
170 fractions = [0.00, aggressive_frac, frac_total]
171 bfs.append(basis_functions.Filter_loaded_basis_function(filternames='u'))
172 bfs.append(basis_functions.Not_twilight_basis_function(sun_alt_limit=sun_alt_limit))
173 bfs.append(basis_functions.Time_to_twilight_basis_function(time_needed=time_needed))
174 bfs.append(basis_functions.Hour_Angle_limit_basis_function(RA=RA, ha_limits=ha_limits))
175 bfs.append(basis_functions.Moon_down_basis_function())
176 bfs.append(basis_functions.Fraction_of_obs_basis_function(frac_total=frac_total, survey_name=survey_name))
177 bfs.append(basis_functions.Look_ahead_ddf_basis_function(frac_total, aggressive_frac,
178 sun_alt_limit=sun_alt_limit, time_needed=time_needed,
179 RA=RA, survey_name=survey_name,
180 ha_limits=ha_limits))
181 bfs.append(basis_functions.Soft_delay_basis_function(fractions=fractions, delays=[0., 0.2, 0.5],
182 survey_name=survey_name))
184 return bfs
187def generate_dd_surveys(nside=None, nexp=2, detailers=None, reward_value=100):
188 """Utility to return a list of standard deep drilling field surveys.
190 XXX-Someone double check that I got the coordinates right!
192 """
194 surveys = []
196 # ELAIS S1
197 RA = 9.45
198 dec = -44.
199 survey_name = 'DD:ELAISS1'
200 ha_limits = ([0., 1.5], [22.5, 24.])
201 bfs = dd_bfs(RA, dec, survey_name, ha_limits)
202 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence='rgizy',
203 nvis=[20, 10, 20, 26, 20],
204 survey_name=survey_name, reward_value=reward_value,
205 nside=nside, nexp=nexp, detailers=detailers))
207 survey_name = 'DD:u,ELAISS1'
208 bfs = dd_u_bfs(RA, dec, survey_name, ha_limits)
210 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence='u',
211 nvis=[8], survey_name=survey_name, reward_value=reward_value, nside=nside,
212 nexp=nexp, detailers=detailers))
214 # XMM-LSS
215 survey_name = 'DD:XMM-LSS'
216 RA = 35.708333
217 dec = -4-45/60.
218 ha_limits = ([0., 1.5], [22.5, 24.])
219 bfs = dd_bfs(RA, dec, survey_name, ha_limits)
221 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence='rgizy',
222 nvis=[20, 10, 20, 26, 20], survey_name=survey_name, reward_value=reward_value,
223 nside=nside, nexp=nexp, detailers=detailers))
224 survey_name = 'DD:u,XMM-LSS'
225 bfs = dd_u_bfs(RA, dec, survey_name, ha_limits)
227 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence='u',
228 nvis=[8], survey_name=survey_name, reward_value=reward_value, nside=nside,
229 nexp=nexp, detailers=detailers))
231 # Extended Chandra Deep Field South
232 # XXX -- this one can pass too close to zenith
233 RA = 53.125
234 dec = -28.-6/60.
235 survey_name = 'DD:ECDFS'
236 ha_limits = [[0.5, 3.0], [20., 22.5]]
237 bfs = dd_bfs(RA, dec, survey_name, ha_limits)
238 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence='rgizy',
239 nvis=[20, 10, 20, 26, 20],
240 survey_name=survey_name, reward_value=reward_value, nside=nside,
241 nexp=nexp, detailers=detailers))
243 survey_name = 'DD:u,ECDFS'
244 bfs = dd_u_bfs(RA, dec, survey_name, ha_limits)
245 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence='u',
246 nvis=[8], survey_name=survey_name, reward_value=reward_value, nside=nside,
247 nexp=nexp, detailers=detailers))
248 # COSMOS
249 RA = 150.1
250 dec = 2.+10./60.+55/3600.
251 survey_name = 'DD:COSMOS'
252 ha_limits = ([0., 1.5], [22.5, 24.])
253 bfs = dd_bfs(RA, dec, survey_name, ha_limits)
254 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence='rgizy',
255 nvis=[20, 10, 20, 26, 20],
256 survey_name=survey_name, reward_value=reward_value, nside=nside,
257 nexp=nexp, detailers=detailers))
258 survey_name = 'DD:u,COSMOS'
259 bfs = dd_u_bfs(RA, dec, survey_name, ha_limits)
260 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence='u',
261 nvis=[8], survey_name=survey_name, reward_value=reward_value, nside=nside,
262 nexp=nexp, detailers=detailers))
264 # Extra DD Field, just to get to 5. Still not closed on this one
265 survey_name = 'DD:290'
266 RA = 349.386443
267 dec = -63.321004
268 ha_limits = ([0., 1.5], [22.5, 24.])
269 bfs = dd_bfs(RA, dec, survey_name, ha_limits)
270 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence='rgizy',
271 nvis=[20, 10, 20, 26, 20],
272 survey_name=survey_name, reward_value=reward_value, nside=nside,
273 nexp=nexp, detailers=detailers))
275 survey_name = 'DD:u,290'
276 bfs = dd_u_bfs(RA, dec, survey_name, ha_limits)
277 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence='u', nvis=[8],
278 survey_name=survey_name, reward_value=reward_value, nside=nside,
279 nexp=nexp, detailers=detailers))
281 return surveys