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

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']
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 flush_pad : float (30.)
38 How long to hold observations in the queue after they were expected to be completed (minutes).
39 """
41 def __init__(self, basis_functions, RA, dec, sequence='rgizy',
42 nvis=[20, 10, 20, 26, 20],
43 exptime=30., u_exptime=30., nexp=2, ignore_obs=None, survey_name='DD',
44 reward_value=None, readtime=2., filter_change_time=120.,
45 nside=None, flush_pad=30., seed=42, detailers=None):
46 super(Deep_drilling_survey, self).__init__(nside=nside, basis_functions=basis_functions,
47 detailers=detailers, ignore_obs=ignore_obs)
48 random.seed(a=seed)
50 self.ra = np.radians(RA)
51 self.ra_hours = RA/360.*24.
52 self.dec = np.radians(dec)
53 self.survey_name = survey_name
54 self.reward_value = reward_value
55 self.flush_pad = flush_pad/60./24. # To days
56 self.filter_sequence = []
57 if type(sequence) == str:
58 self.observations = []
59 for num, filtername in zip(nvis, sequence):
60 for j in range(num):
61 obs = empty_observation()
62 obs['filter'] = filtername
63 if filtername == 'u':
64 obs['exptime'] = u_exptime
65 else:
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 else:
73 self.observations = sequence
75 # Let's just make this an array for ease of use
76 self.observations = np.concatenate(self.observations)
77 order = np.argsort(self.observations['filter'])
78 self.observations = self.observations[order]
80 n_filter_change = np.size(np.unique(self.observations['filter']))
82 # Make an estimate of how long a seqeunce will take. Assumes no major rotational or spatial
83 # dithering slowing things down.
84 self.approx_time = np.sum(self.observations['exptime']+readtime*self.observations['nexp'])/3600./24. \
85 + filter_change_time*n_filter_change/3600./24. # to days
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 # Set the flush_by
123 result['flush_by_mjd'] = conditions.mjd + self.approx_time + self.flush_pad
125 # remove filters that are not mounted
126 mask = np.isin(result['filter'], conditions.mounted_filters)
127 result = result[mask]
128 # Put current loaded filter first
129 ind1 = np.where(result['filter'] == conditions.current_filter)[0]
130 ind2 = np.where(result['filter'] != conditions.current_filter)[0]
131 result = result[ind1.tolist() + (ind2.tolist())]
133 # convert to list of array. Arglebargle, don't understand why I need a reshape there
134 final_result = [row.reshape(1,) for row in result]
135 result = final_result
137 return result
140def dd_bfs(RA, dec, survey_name, ha_limits, frac_total=0.0185/2., aggressive_frac=0.011/2.,
141 delays=[0., 0.5, 1.5]):
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.Not_twilight_basis_function(sun_alt_limit=sun_alt_limit))
150 bfs.append(basis_functions.Time_to_twilight_basis_function(time_needed=time_needed))
151 bfs.append(basis_functions.Hour_Angle_limit_basis_function(RA=RA, ha_limits=ha_limits))
152 bfs.append(basis_functions.Moon_down_basis_function())
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=delays,
159 survey_name=survey_name))
161 return bfs
164def generate_dd_surveys(nside=None, nexp=2, detailers=None, reward_value=100,
165 frac_total=0.0185/2., aggressive_frac=0.011/2., exptime=30, u_exptime=30,
166 nvis_master=[8, 20, 10, 20, 26, 20], delays=[0., 0.5, 1.5]):
167 """Utility to return a list of standard deep drilling field surveys.
169 XXX-Someone double check that I got the coordinates right!
171 """
173 surveys = []
175 # ELAIS S1
176 RA = 9.45
177 dec = -44.
178 survey_name = 'DD:ELAISS1'
179 ha_limits = ([0., 1.5], [21.5, 24.])
180 bfs = dd_bfs(RA, dec, survey_name, ha_limits, frac_total=frac_total, aggressive_frac=aggressive_frac, delays=delays)
181 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence='urgizy',
182 nvis=nvis_master, exptime=exptime, u_exptime=u_exptime,
183 survey_name=survey_name, reward_value=reward_value,
184 nside=nside, nexp=nexp, detailers=detailers))
186 # XMM-LSS
187 survey_name = 'DD:XMM-LSS'
188 RA = 35.708333
189 dec = -4-45/60.
190 ha_limits = ([0., 1.5], [21.5, 24.])
191 bfs = dd_bfs(RA, dec, survey_name, ha_limits, frac_total=frac_total, aggressive_frac=aggressive_frac, delays=delays)
193 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence='urgizy', exptime=exptime, u_exptime=u_exptime,
194 nvis=nvis_master, survey_name=survey_name, reward_value=reward_value,
195 nside=nside, nexp=nexp, detailers=detailers))
197 # Extended Chandra Deep Field South
198 RA = 53.125
199 dec = -28.-6/60.
200 survey_name = 'DD:ECDFS'
201 ha_limits = [[0.5, 3.0], [20., 22.5]]
202 bfs = dd_bfs(RA, dec, survey_name, ha_limits, frac_total=frac_total, aggressive_frac=aggressive_frac, delays=delays)
203 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence='urgizy',
204 nvis=nvis_master, exptime=exptime, u_exptime=u_exptime,
205 survey_name=survey_name, reward_value=reward_value, nside=nside,
206 nexp=nexp, detailers=detailers))
208 # COSMOS
209 RA = 150.1
210 dec = 2.+10./60.+55/3600.
211 survey_name = 'DD:COSMOS'
212 ha_limits = ([0., 2.5], [21.5, 24.])
213 bfs = dd_bfs(RA, dec, survey_name, ha_limits, frac_total=frac_total, aggressive_frac=aggressive_frac, delays=delays)
214 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence='urgizy',
215 nvis=nvis_master, exptime=exptime, u_exptime=u_exptime,
216 survey_name=survey_name, reward_value=reward_value, nside=nside,
217 nexp=nexp, detailers=detailers))
219 # Euclid Fields
220 # I can use the sequence kwarg to do two positions per sequence
221 filters = 'urgizy'
222 nviss = nvis_master
223 survey_name = 'DD:EDFS'
224 # Note the sequences need to be in radians since they are using observation objects directly
225 RAs = np.radians([58.97, 63.6])
226 decs = np.radians([-49.28, -47.60])
227 sequence = []
229 for filtername, nvis in zip(filters, nviss):
230 for ra, dec in zip(RAs, decs):
231 for num in range(nvis):
232 obs = empty_observation()
233 obs['filter'] = filtername
234 if filtername == 'u':
235 obs['exptime'] = u_exptime
236 else:
237 obs['exptime'] = exptime
238 obs['RA'] = ra
239 obs['dec'] = dec
240 obs['nexp'] = nexp
241 obs['note'] = survey_name
242 sequence.append(obs)
244 ha_limits = ([0., 1.5], [22.5, 24.])
245 # And back to degrees for the basis function
246 bfs = dd_bfs(np.degrees(RAs[0]), np.degrees(decs[0]), survey_name, ha_limits,
247 frac_total=frac_total, aggressive_frac=aggressive_frac, delays=delays)
248 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence=sequence,
249 survey_name=survey_name, reward_value=reward_value, nside=nside,
250 nexp=nexp, detailers=detailers))
252 return surveys