ctools  2.0.0
 All Classes Namespaces Files Functions Variables Macros Pages
csfindobs.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # ==========================================================================
3 # Find observations from an IACT data store
4 #
5 # Copyright (C) 2016-2022 Michael Mayer
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 os
23 import json
24 import gammalib
25 import ctools
26 
27 
28 # =============== #
29 # csfindobs class #
30 # =============== #
31 class csfindobs(ctools.cscript):
32  """
33  Find observations from an IACT data store
34  """
35 
36  # Constructor
37  def __init__(self, *argv):
38  """
39  Constructor
40  """
41  # Initialise application by calling the base class constructor
42  self._init_cscript(self.__class__.__name__, ctools.__version__, argv)
43 
44  # Set name
45  self._datapath = os.getenv('VHEFITS','')
46  self._prodname = ''
47  self._select_radec = True
48  self._radius = 0.0
49  self._ra = 0.0
50  self._dec = 0.0
51  self._obs_index = ''
52  self._runs = []
53 
54  # Return
55  return
56 
57 
58  # Private methods
59  def _get_parameters(self):
60  """
61  Get parameters from parfile and setup the observation.
62  """
63  # Query datapath. If the parameter is not NONE then use it, otherwise
64  # use the datapath from the VHEFITS environment variable
65  datapath = self['datapath'].string()
66  if gammalib.toupper(datapath) != 'NONE':
67  self._datapath = datapath
68  else:
69  self._datapath = os.getenv('VHEFITS','')
70 
71  # Expand environment
72  self._datapath = gammalib.expand_env(self._datapath)
73 
74  # Get production name
75  self._prodname = self['prodname'].string()
76 
77  # Master index file name
78  master_indx = self['master_indx'].string()
79 
80  # Initialise flag if spatial selection is required
81  self._select_radec = True
82 
83  # Initialise invalid radius
84  self._radius = 0.0
85 
86  # Check for validity of spatial parameters
87  if (self['ra'].is_valid() and
88  self['dec'].is_valid() and
89  self['rad'].is_valid()):
90 
91  # Read spatial parameters
92  self._ra = self['ra'].real()
93  self._dec = self['dec'].real()
94  self._radius = self['rad'].real()
95 
96  # ... otherwise signal that there are no spatial parameters for
97  # selection
98  else:
99  self._select_radec = False
100 
101  # Check Radius for validity
102  if self._radius <= 0.0:
103  self._select_radec = False
104 
105  # Query other parameters
106  self['min_qual'].integer()
107  self['expression'].string()
108 
109  # Read ahead output parameters
110  if self._read_ahead():
111  self['outfile'].filename()
112 
113  # Set filename of JSON master file and raise an exception if the file
114  # does not exist
115  master_file = os.path.join(self._datapath, master_indx)
116  if not os.path.isfile(master_file):
117  msg = ('FITS data store not available. No master index file found '
118  'at "%s". Make sure the file is copied from the server and '
119  'your datapath is set correctly.' % master_file)
120  raise RuntimeError(msg)
121 
122  # Open and load JSON master file. If the "dataset" key is not available
123  # then raise an exception
124  json_data = open(master_file).read()
125  data = json.loads(json_data)
126  if not 'datasets' in data:
127  msg = ('Key "datasets" not available in master index file.')
128  raise RuntimeError(msg)
129 
130  # Get configurations from JSON master file
131  configs = data['datasets']
132 
133  # Initialise obs index file
134  self._obs_index = ''
135 
136  # Get name of observation index file
137  for config in configs:
138  if self._prodname == config['name']:
139  self._obs_index = str(os.path.join(self._datapath,
140  config['obsindx']))
141  break
142 
143  # If the observation index file name is empty then raise an exception
144  if self._obs_index == '':
145  msg = ('FITS data store "%s" not available. Run csiactdata to get '
146  'a list of available storage names.' % self._prodname)
147  raise RuntimeError(msg)
148 
149  # If the observation index file is not a FITS file then raise an
150  # exception
151  filename = gammalib.GFilename(self._obs_index+'[OBS_INDEX]')
152  if not filename.is_fits():
153  msg = ('Observation index file "%s[OBS_INDEX]" for FITS data store '
154  '"%s" not available. Check your master index file or run '
155  'csiactdata to get a list of available storage names.' %
156  (self._obs_index, self._prodname))
157  raise RuntimeError(msg)
158 
159  # Write input parameters into logger
160  self._log_parameters(gammalib.TERSE)
161 
162  # Return
163  return
164 
165 
166  # Public methods
167  def process(self):
168  """
169  Process the script
170  """
171  # Get parameters
172  self._get_parameters()
173 
174  # Write header into logger
175  self._log_header1(gammalib.TERSE, 'Find observations')
176 
177  # Initialise run list
178  self._runs = []
179 
180  # Initialise selection expression
181  expr = ''
182 
183  # If spatial selection is requested then add an angular separation
184  # expression to the expression string
185  if self._select_radec:
186  expr += 'ANGSEP('+str(self._ra)+','+str(self._dec)+ \
187  ',RA_PNT,DEC_PNT)<='+str(self._radius)
188 
189  # Add '&&' connector if expression is not empty
190  if len(expr):
191  expr += '&&'
192 
193  # Add always quality expression
194  expr += 'QUALITY<='+str(self['min_qual'].integer())
195 
196  # Add user expression is one has been specified
197  expression = self['expression'].string()
198  if (expression != 'NONE' and expression != 'INDEF' and
199  len(expression) > 0):
200  expr += '&&' + expression
201 
202  # Write the expression into the logger
203  self._log_value(gammalib.NORMAL, 'Expression', expr)
204 
205  # Set filename including the selection expression
206  filename = self._obs_index+'[OBS_INDEX]['+expr+']'
207 
208  # Open observation index FITS file
209  fits = gammalib.GFits(filename)
210 
211  # If there are entries in the observation index table then append the
212  # observations identifiers to the run list
213  obs_index = fits['OBS_INDEX']
214  if obs_index.nrows() > 0:
215  for i in range(obs_index.nrows()):
216  self._runs.append(obs_index['OBS_ID'][i])
217 
218  # Write the number of observations into the logger
219  self._log_value(gammalib.TERSE, 'Observations', len(self._runs))
220 
221  # Write the observation identifiers into the logger
222  if len(self._runs) > 0:
223  for i, run in enumerate(self._runs):
224  self._log_value(gammalib.NORMAL, 'Observation %d' % (i+1), run)
225 
226  # Close FITS file
227  fits.close()
228 
229  # Return
230  return
231 
232  def save(self):
233  """
234  Save runlist
235  """
236  # Write header
237  self._log_header1(gammalib.TERSE, 'Save runlist')
238 
239  # Get filename
240  outfile = self['outfile'].filename()
241 
242  # If the runlist file exists but the clobber flag is not set then signal
243  # that runlist file exists already
244  if outfile.exists() and not self._clobber():
245  msg = ('File "'+outfile+'" exists already, runlist not saved. '
246  'Set clobber=yes to overwrite the file.\n')
247  self._log_string(gammalib.TERSE, msg)
248 
249  # ... otherwise save the runlist into a file
250  else:
251 
252  # Log file name
253  self._log_value(gammalib.NORMAL, 'Runlist file', outfile.url())
254 
255  # Write all runs to file
256  f = open(outfile.url(),'w')
257  for run in self._runs:
258  f.write(str(run)+' \n')
259  f.close()
260 
261  # Return
262  return
263 
264  def runs(self):
265  """
266  Return run list
267 
268  Returns
269  -------
270  runs : list of str
271  A list of runs
272  """
273  # Return
274  return self._runs
275 
276 
277 # ======================== #
278 # Main routine entry point #
279 # ======================== #
280 if __name__ == '__main__':
281 
282  # Create instance of application
283  app = csfindobs(sys.argv)
284 
285  # Execute application
286  app.execute()