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))
160 bfs.append(basis_functions.Time_to_scheduled_basis_function(time_needed=time_needed))
162 return bfs
165def generate_dd_surveys(nside=None, nexp=2, detailers=None, euclid_detailers=None, reward_value=100,
166 frac_total=0.0185/2., aggressive_frac=0.011/2., exptime=30, u_exptime=30,
167 nvis_master=[8, 20, 10, 20, 26, 20], delays=[0., 0.5, 1.5]):
168 """Utility to return a list of standard deep drilling field surveys.
170 XXX-Someone double check that I got the coordinates right!
172 """
174 if euclid_detailers is None:
175 euclid_detailers = detailers
177 surveys = []
179 # ELAIS S1
180 RA = 9.45
181 dec = -44.
182 survey_name = 'DD:ELAISS1'
183 ha_limits = ([0., 1.5], [21.5, 24.])
184 bfs = dd_bfs(RA, dec, survey_name, ha_limits, frac_total=frac_total, aggressive_frac=aggressive_frac, delays=delays)
185 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence='urgizy',
186 nvis=nvis_master, exptime=exptime, u_exptime=u_exptime,
187 survey_name=survey_name, reward_value=reward_value,
188 nside=nside, nexp=nexp, detailers=detailers))
190 # XMM-LSS
191 survey_name = 'DD:XMM-LSS'
192 RA = 35.708333
193 dec = -4-45/60.
194 ha_limits = ([0., 1.5], [21.5, 24.])
195 bfs = dd_bfs(RA, dec, survey_name, ha_limits, frac_total=frac_total, aggressive_frac=aggressive_frac, delays=delays)
197 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence='urgizy', exptime=exptime, u_exptime=u_exptime,
198 nvis=nvis_master, survey_name=survey_name, reward_value=reward_value,
199 nside=nside, nexp=nexp, detailers=detailers))
201 # Extended Chandra Deep Field South
202 RA = 53.125
203 dec = -28.-6/60.
204 survey_name = 'DD:ECDFS'
205 ha_limits = [[0.5, 3.0], [20., 22.5]]
206 bfs = dd_bfs(RA, dec, survey_name, ha_limits, frac_total=frac_total, aggressive_frac=aggressive_frac, delays=delays)
207 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence='urgizy',
208 nvis=nvis_master, exptime=exptime, u_exptime=u_exptime,
209 survey_name=survey_name, reward_value=reward_value, nside=nside,
210 nexp=nexp, detailers=detailers))
212 # COSMOS
213 RA = 150.1
214 dec = 2.+10./60.+55/3600.
215 survey_name = 'DD:COSMOS'
216 ha_limits = ([0., 2.5], [21.5, 24.])
217 bfs = dd_bfs(RA, dec, survey_name, ha_limits, frac_total=frac_total, aggressive_frac=aggressive_frac, delays=delays)
218 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence='urgizy',
219 nvis=nvis_master, exptime=exptime, u_exptime=u_exptime,
220 survey_name=survey_name, reward_value=reward_value, nside=nside,
221 nexp=nexp, detailers=detailers))
223 # Euclid Fields
224 # I can use the sequence kwarg to do two positions per sequence
225 filters = 'urgizy'
226 nviss = nvis_master
227 survey_name = 'DD:EDFS'
228 # Note the sequences need to be in radians since they are using observation objects directly
229 # Coords from jc.cuillandre@cea.fr Oct 15, 2020
230 RAs = np.radians([58.90, 63.6])
231 decs = np.radians([-49.315, -47.60])
232 suffixes = [', a', ', b']
233 sequence = []
235 for filtername, nvis in zip(filters, nviss):
236 for ra, dec, suffix in zip(RAs, decs, suffixes):
237 for num in range(nvis):
238 obs = empty_observation()
239 obs['filter'] = filtername
240 if filtername == 'u':
241 obs['exptime'] = u_exptime
242 else:
243 obs['exptime'] = exptime
244 obs['RA'] = ra
245 obs['dec'] = dec
246 obs['nexp'] = nexp
247 obs['note'] = survey_name + suffix
248 sequence.append(obs)
250 ha_limits = ([0., 1.5], [22.5, 24.])
251 # And back to degrees for the basis function
252 bfs = dd_bfs(np.degrees(RAs[0]), np.degrees(decs[0]), survey_name, ha_limits,
253 frac_total=frac_total, aggressive_frac=aggressive_frac, delays=delays)
254 surveys.append(Deep_drilling_survey(bfs, RA, dec, sequence=sequence,
255 survey_name=survey_name, reward_value=reward_value, nside=nside,
256 nexp=nexp, detailers=euclid_detailers))
258 return surveys