ctools  2.0.0
 All Classes Namespaces Files Functions Variables Macros Pages
csobsselect.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # ==========================================================================
3 # Selects observations from an observation definition XML file
4 #
5 # Copyright (C) 2016-2022 Juergen Knoedlseder
6 #
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #
20 # ==========================================================================
21 import sys
22 import gammalib
23 import ctools
24 
25 
26 # ================= #
27 # csobsselect class #
28 # ================= #
29 class csobsselect(ctools.csobservation):
30  """
31  Selects observations from an observation definition XML file
32  """
33 
34  # Constructor
35  def __init__(self, *argv):
36  """
37  Constructor
38  """
39  # Initialise application by calling the appropriate class constructor
40  self._init_csobservation(self.__class__.__name__, ctools.__version__, argv)
41 
42  # Return
43  return
44 
45 
46  # Private methods
47  def _get_parameters(self):
48  """
49  Get parameters from parfile and setup observations
50  """
51  # If there are no observations in container then query the inobs
52  # parameter
53  if self.obs().is_empty():
54  self['inobs'].filename()
55 
56  # Query relevant pointing selection parameters
57  pntselect = self['pntselect'].string()
58  coordsys = self['coordsys'].string()
59  if coordsys == 'CEL':
60  self['ra'].real()
61  self['dec'].real()
62  else:
63  self['glon'].real()
64  self['glat'].real()
65  if pntselect == 'CIRCLE':
66  self['rad'].real()
67  else:
68  self['width'].real()
69  self['height'].real()
70 
71  # Query time interval. The GammaLib time reference is specified as a
72  # dummy argument since the relevant intervals will be queried later
73  # using the time reference for each observation
74  self._get_gti(gammalib.GTimeReference())
75 
76  # Query ahead output model filename
77  if self._read_ahead():
78  self['outobs'].filename()
79 
80  # If there are no observations in container then get them from the
81  # parameter file
82  if self.obs().is_empty():
83  self.obs(self._get_observations(False))
84 
85  # Write input parameters into logger
86  self._log_parameters(gammalib.TERSE)
87 
88  # Return
89  return
90 
91  def _log_selection(self, obs, msg):
92  """
93  Log observation selection
94 
95  Parameters
96  ----------
97  obs : `~gammalib.GObservation`
98  Observation
99  msg : str
100  Message
101  """
102  # Set observation name
103  if len(obs.name()) > 0:
104  name = obs.name()
105  else:
106  name = 'Observation'
107 
108  # Prepend obervation ID
109  if len(obs.id()) > 0:
110  msg = obs.id()+' '+msg
111 
112  # Log observation selection
113  self._log_value(gammalib.NORMAL, name, msg)
114 
115  # Return
116  return
117 
118  def _select_observation(self, obs):
119  """
120  Select observation
121 
122  Parameters
123  ----------
124  obs : `~gammalib.GObservation`
125  Observation
126 
127  Returns
128  -------
129  select : bool
130  Observation selection flag
131  """
132  # Initialise selection flag
133  select = False
134 
135  # Continue only if observation is a CTA observation
136  if obs.classname() == 'GCTAObservation':
137 
138  # Get observation start and stop time
139  obs_gti = obs.gti()
140  obs_tstart = obs_gti.tstart()
141  obs_tstop = obs_gti.tstop()
142 
143  # Get User start and stop time in the time reference of the CTA
144  # observations
145  gti = self._get_gti(obs_gti.reference())
146  tstart = gti.tstart()
147  tstop = gti.tstop()
148 
149  # If there is a valid User start and stop time and the observation
150  # stop is before the start time or the observation start after the
151  # stop time, then skip the observation
152  if gti.size() > 0 and (obs_tstop < tstart or obs_tstart > tstop):
153  self._log_selection(obs, 'outside time interval')
154 
155  # ... otherwise select spatially
156  else:
157 
158  # Get selection type
159  pntselect = self['pntselect'].string()
160 
161  # Dispatch according to selection type
162  if pntselect == 'CIRCLE':
163  select = self._select_circle(obs)
164  else:
165  select = self._select_box(obs)
166 
167  # Return selection flag
168  return select
169 
170  def _select_circle(self, obs):
171  """
172  Select observation in a pointing circle
173 
174  Parameters
175  ----------
176  obs : `~gammalib.GCTAObservation`
177  CTA observation
178 
179  Returns
180  -------
181  select : bool
182  Observation selection flag
183  """
184  # Get coordinate system of selection circle
185  coordsys = self['coordsys'].string()
186 
187  # Get pointing direction
188  pnt = obs.pointing().dir()
189 
190  # Set selection circle centre
191  centre = gammalib.GSkyDir()
192  if coordsys == 'CEL':
193  centre.radec_deg(self['ra'].real(), self['dec'].real())
194  else:
195  centre.lb_deg(self['glon'].real(), self['glat'].real())
196 
197  # Set selection flag according to distance
198  if centre.dist_deg(pnt) <= self['rad'].real():
199  select = True
200  msg = 'inside selection circle'
201  else:
202  select = False
203  msg = 'outside selection circle'
204 
205  # Log observation selection
206  self._log_selection(obs, msg)
207 
208  # Return selection flag
209  return select
210 
211  def _select_box(self, obs):
212  """
213  Select observation in a pointing box
214 
215  Parameters
216  ----------
217  obs : `~gammalib.GCTAObservation`
218  CTA observation
219 
220  Returns
221  -------
222  select : bool
223  Observation selection flag
224  """
225  # Initialise selection flag
226  select = False
227  msg = 'outside selection box'
228 
229  # Get coordinate system of selection circle
230  coordsys = self['coordsys'].string()
231 
232  # Get pointing direction
233  pnt = obs.pointing().dir()
234 
235  # Get selection box width and height
236  width = self['width'].real()
237  height = self['height'].real()
238 
239  # Make selection for celestial coordinates ...
240  if coordsys == 'CEL':
241 
242  # Determine box boundaries
243  ra = self['ra'].real()
244  dec = self['dec'].real()
245  ra_min = ra - 0.5 * width
246  ra_max = ra + 0.5 * width
247  dec_min = dec - 0.5 * height
248  dec_max = dec + 0.5 * height
249 
250  # Check if pointing lies within boundaries
251  ra = pnt.ra_deg()
252  ra_plus = ra + 360.0
253  ra_minus = ra - 360.0
254  dec = pnt.dec_deg()
255  if dec >= dec_min and dec <= dec_max:
256  if ra >= ra_min and ra <= ra_max or \
257  ra_plus >= ra_min and ra_plus <= ra_max or \
258  ra_minus >= ra_min and ra_minus <= ra_max:
259  select = True
260  msg = 'inside selection box'
261 
262  # ... or galactic coordinates
263  else:
264 
265  # Determine box boundaries
266  glon = self['glon'].real()
267  glat = self['glat'].real()
268  glon_min = glon - 0.5 * width
269  glon_max = glon + 0.5 * width
270  glat_min = glat - 0.5 * height
271  glat_max = glat + 0.5 * height
272 
273  # Check if pointing lies within boundaries
274  glon = pnt.l_deg()
275  glon_plus = glon + 360.0
276  glon_minus = glon - 360.0
277  glat = pnt.b_deg()
278  if glat >= glat_min and glat <= glat_max:
279  if glon >= glon_min and glon <= glon_max or \
280  glon_plus >= glon_min and glon_plus <= glon_max or \
281  glon_minus >= glon_min and glon_minus <= glon_max:
282  select = True
283  msg = 'inside selection box'
284 
285  # Log observation selection
286  self._log_selection(obs, msg)
287 
288  # Return selection flag
289  return select
290 
291 
292  # Public methods
293  def process(self):
294  """
295  Process the script
296  """
297  # Get parameters
298  self._get_parameters()
299 
300  # Initialise empty observation container for selected observations
301  selected_obs = gammalib.GObservations()
302 
303  # Set models
304  selected_obs.models(self.obs().models())
305 
306  # Write input observation container into logger
307  self._log_observations(gammalib.NORMAL, self.obs(), 'Input observation')
308 
309  # Write header
310  self._log_header1(gammalib.TERSE, 'Observation selection')
311 
312  # Loop over observations
313  for obs in self.obs():
314 
315  # If observation is selected then append observation
316  if self._select_observation(obs):
317  selected_obs.append(obs)
318 
319  # Copy selected observations into observation
320  self.obs(selected_obs)
321 
322  # Write input observation container into logger
323  self._log_observations(gammalib.NORMAL, self.obs(), 'Selected observation')
324 
325  # Return
326  return
327 
328  def save(self):
329  """
330  Save observation definition XML file
331  """
332  # Write header
333  self._log_header1(gammalib.TERSE, 'Save observations')
334 
335  # Get output filename
336  outobs = self['outobs'].filename()
337 
338  # If file exists and clobber flag is false then raise an exception
339  if outobs.exists() and not self._clobber:
340  msg = ('Cannot save "'+outobs.url()+'": File already exists. '
341  'Use parameter clobber=yes to allow overwriting of files.')
342  raise RuntimeError(msg)
343 
344  # Otherwise log filename and save file
345  else:
346  # Log filename
347  self._log_value(gammalib.NORMAL, 'Obs. definition XML file',
348  outobs.url())
349 
350  # Save observations
351  self.obs().save(outobs)
352 
353  # Return
354  return
355 
356 
357 # ======================== #
358 # Main routine entry point #
359 # ======================== #
360 if __name__ == '__main__':
361 
362  # Create instance of application
363  app = csobsselect(sys.argv)
364 
365  # Execute application
366  app.execute()