ctools 2.1.0.dev
Loading...
Searching...
No Matches
csobsdef.py
Go to the documentation of this file.
1#! /usr/bin/env python
2# ==========================================================================
3# Generation of an observation definition file
4#
5# Copyright (C) 2015-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# ==========================================================================
21import sys
22import gammalib
23import ctools
24
25
26# ============== #
27# csobsdef class #
28# ============== #
29class csobsdef(ctools.cscript):
30 """
31 Creates an observation definition file from a pointing list
32
33 The pointing list is a comma-separated value ASCII file with header
34 keywords in the first row followed by a list of pointings (one
35 pointing per row). The following header keywords are supported (case
36 sensitive, column order irrelevant):
37
38 name - Observation name string
39 id - Unique observation identifier string
40 ra - Right Ascension of pointing (deg)
41 dec - Declination of pointing (deg)
42 lon - Galactic longitude of pointing (deg)
43 lat - Galactic latitude of pointing (deg)
44 tmin - Start of pointing (seconds)
45 duration - Duration of pointing (seconds)
46 emin - Lower energy limit (TeV)
47 emax - Upper energy limit (TeV)
48 rad - Radius of region of interest (deg)
49 deadc - Deadtime correction factor [0-1]
50 instrument - Name of Cherenkov telescope
51 caldb - Calibration database
52 irf - Response function name
53
54 Only the pairs (ra,dec) or (lon,lat) are mandatory header keywords.
55 All other keywords are optional and can be specified when calling
56 csobsdef as user parameters. The only exception is the "duration"
57 keyword that will automatically be queried.
58
59 Examples:
60
61 ./csobsdef
62 Creates minimal observation definition file.
63
64 ./csobsdef emin=0.1 emax=100.0
65 Creates observation definition file with an energy range
66 100 GeV - 100 TeV.
67
68 ./csobsdef rad=5
69 Creates observation definition file with a ROI radius of
70 5 degrees.
71
72 ./csobsdef caldb=prod2 irf=South_50h
73 Creates observation definition file using the "South_50h"
74 IRF in the "prod2" calibration database of CTA.
75 """
76
77 # Constructor
78 def __init__(self, *argv):
79 """
80 Constructor
81
82 Parameters
83 ----------
84 argv : list of str
85 List of IRAF command line parameter strings of the form
86 ``parameter=3``.
87 """
88 # Initialise application by calling the base class constructor
89 self._init_cscript(self.__class__.__name__, ctools.__version__, argv)
90
91 # Initialise class members
92 self._obs = gammalib.GObservations()
93 self._pntdef = gammalib.GCsv()
94 self._tmin = 0.0
95
96 # Return
97 return
98
99
100 # Private methods
102 """
103 Get parameters from parfile
104 """
105 # Query input filename if necessary
106 if self._pntdef.size() == 0:
107 self['inpnt'].filename()
108
109 # Read ahead parameters
110 if self._read_ahead():
111 self['outobs'].query()
112
113 # Write input parameters into logger
114 self._log_parameters(gammalib.TERSE)
115
116 # Return
117 return
118
119 def _set_irf(self, obs, caldb, irf):
120 """
121 Set response for an observation
122
123 The method creates an XML element so that that the response XML
124 writer will write the database and response name into the
125 observation definition file.
126
127 Parameters
128 ----------
129 obs : `~gammalib.GCTAObservation`
130 CTA observation
131 caldb : str
132 Calibration database
133 irf : str
134 Instrument response function
135
136 Returns
137 -------
138 obs : `~gammalib.GCTAObservation`
139 CTA observation with response attached
140 """
141 # Create XML element
142 xml = gammalib.GXmlElement()
143
144 # Append parameter
145 parameter = 'parameter name="Calibration" database="'+caldb+\
146 '" response="'+irf+'"'
147 xml.append(gammalib.GXmlElement(parameter))
148
149 # Create CTA response
150 response = gammalib.GCTAResponseIrf()
151 response.read(xml)
152
153 # Attach response to observation
154 obs.response(response)
155
156 # Return observation
157 return obs
158
159
160 # Public methods
161 def process(self):
162 """
163 Process the script
164
165 Raises
166 ------
167 RuntimeError
168 Invalid pointing definition file format
169 """
170 # Get parameters
171 self._get_parameters()
172
173 # Write header into logger
174 self._log_header1(gammalib.TERSE,
175 'Creating observation definition XML file')
176
177 # Load pointing definition file if it is not already set. Extract
178 # the number of columns and pointings
179 if self._pntdef.size() == 0:
180 self._pntdef = gammalib.GCsv(self['inpnt'].filename(), ',')
181 ncols = self._pntdef.ncols()
182 npnt = self._pntdef.nrows()-1
183
184 # Raise an exception if there is no header information
185 if self._pntdef.nrows() < 1:
186 raise RuntimeError('No header found in pointing definition file.')
187
188 # Clear observation container
189 self._obs.clear()
190
191 # Initialise observation identifier counter
192 identifier = 1
193
194 # Extract header columns from pointing definition file and put them
195 # into a list
196 header = []
197 for col in range(ncols):
198 header.append(self._pntdef[0,col])
199
200 # Loop over all pointings
201 for pnt in range(npnt):
202
203 # Set pointing definition CSV file row index
204 row = pnt + 1
205
206 # Create empty CTA observation
207 obs = gammalib.GCTAObservation()
208
209 # Set instrument
210 if 'instrument' in header:
211 instrument = self._pntdef[row, header.index('instrument')]
212 else:
213 instrument = self['instrument'].string()
214 instrument = gammalib.toupper(instrument)
215 obs.instrument(instrument)
216
217 # Set observation name. If no observation name was given then
218 # use "None".
219 if 'name' in header:
220 name = self._pntdef[row, header.index('name')]
221 else:
222 name = self['name'].string()
223 obs.name(name)
224
225 # Set observation identifier. If no observation identified was
226 # given the use the internal counter.
227 if 'id' in header:
228 obsid = self._pntdef[row, header.index('id')]
229 else:
230 obsid = '%6.6d' % identifier
231 identifier += 1
232 obs.id(obsid)
233
234 # Set pointing. Either use "ra" and "dec" or "lon" and "lat".
235 # If none of these pairs are given then raise an exception.
236 if 'ra' in header and 'dec' in header:
237 ra = float(self._pntdef[row, header.index('ra')])
238 dec = float(self._pntdef[row, header.index('dec')])
239 pntdir = gammalib.GSkyDir()
240 pntdir.radec_deg(ra,dec)
241 elif 'lon' in header and 'lat' in header:
242 lon = float(self._pntdef[row, header.index('lon')])
243 lat = float(self._pntdef[row, header.index('lat')])
244 pntdir = gammalib.GSkyDir()
245 pntdir.lb_deg(lon,lat)
246 else:
247 raise RuntimeError('No (ra,dec) or (lon,lat) columns '
248 'found in pointing definition file.')
249 obs.pointing(gammalib.GCTAPointing(pntdir))
250
251 # Set response function. If no "caldb" or "irf" information is
252 # provided then use the user parameter values.
253 if 'caldb' in header:
254 caldb = self._pntdef[row, header.index('caldb')]
255 else:
256 caldb = self['caldb'].string()
257 if 'irf' in header:
258 irf = self._pntdef[row, header.index('irf')]
259 else:
260 irf = self['irf'].string()
261 if caldb != '' and irf != '':
262 obs = self._set_irf(obs, caldb, irf)
263
264 # Set deadtime correction factor. If no information is provided
265 # then use the user parameter value "deadc".
266 if 'deadc' in header:
267 deadc = float(self._pntdef[row, header.index('deadc')])
268 else:
269 deadc = self['deadc'].real()
270 obs.deadc(deadc)
271
272 # Set Good Time Interval. If no information is provided then use
273 # the user parameter values "tmin" and "duration".
274 if 'tmin' in header:
275 self._tmin = float(self._pntdef[row, header.index('tmin')])
276 if 'duration' in header:
277 duration = float(self._pntdef[row, header.index('duration')])
278 else:
279 duration = self['duration'].real()
280 tref = gammalib.GTimeReference(self['mjdref'].real(),'s')
281 tmin = self._tmin
282 tmax = self._tmin + duration
283 gti = gammalib.GGti(tref)
284 tstart = gammalib.GTime(tmin, tref)
285 tstop = gammalib.GTime(tmax, tref)
286 self._tmin = tmax
287 gti.append(tstart, tstop)
288 obs.ontime(gti.ontime())
289 obs.livetime(gti.ontime()*deadc)
290
291 # Set Energy Boundaries. If no "emin" or "emax" information is
292 # provided then use the user parameter values in case they are
293 # valid.
294 has_emin = False
295 has_emax = False
296 if 'emin' in header:
297 emin = float(self._pntdef[row, header.index('emin')])
298 has_emin = True
299 else:
300 if self['emin'].is_valid():
301 emin = self['emin'].real()
302 has_emin = True
303 if 'emax' in header:
304 emax = float(self._pntdef[row, header.index('emax')])
305 has_emax = True
306 else:
307 if self['emax'].is_valid():
308 emax = self['emax'].real()
309 has_emax = True
310 has_ebounds = has_emin and has_emax
311 if has_ebounds:
312 ebounds = gammalib.GEbounds(gammalib.GEnergy(emin, 'TeV'),
313 gammalib.GEnergy(emax, 'TeV'))
314
315 # Set ROI. If no ROI radius is provided then use the user
316 # parameters "rad".
317 has_roi = False
318 if 'rad' in header:
319 rad = float(self._pntdef[row, header.index('rad')])
320 has_roi = True
321 else:
322 if self['rad'].is_valid():
323 rad = self['rad'].real()
324 has_roi = True
325 if has_roi:
326 roi = gammalib.GCTARoi(gammalib.GCTAInstDir(pntdir), rad)
327
328 # Create an empty event list
329 event_list = gammalib.GCTAEventList()
330 event_list.gti(gti)
331
332 # If available, set the energy boundaries and the ROI
333 if has_ebounds:
334 event_list.ebounds(ebounds)
335 if has_roi:
336 event_list.roi(roi)
337
338 # Attach event list to CTA observation
339 obs.events(event_list)
340
341 # Write observation into logger
342 name = obs.instrument()+' observation'
343 value = 'Name="%s" ID="%s"' % (obs.name(), obs.id())
344 self._log_value(gammalib.NORMAL, name, value)
345 self._log_string(gammalib.EXPLICIT, str(obs)+'\n')
346
347 # Append observation
348 self._obs.append(obs)
349
350 # Return
351 return
352
353 def save(self):
354 """
355 Save observation definition XML file.
356 """
357 # Write header and filename into logger
358 self._log_header1(gammalib.TERSE, 'Save observation definition XML file')
359
360 # Check if observation definition XML file is valid
361 if self['outobs'].is_valid():
362
363 # Get output filename in case it was not read ahead
364 outobs = self['outobs'].filename()
365
366 # Log filename
367 self._log_value(gammalib.NORMAL, 'Observation XML file', outobs.url())
368
369 # Save observation definition XML file
370 self._obs.save(outobs)
371
372 # Return
373 return
374
375 def obs(self):
376 """
377 Returns observation container
378 -----
379 Returns
380 obs : `~gammalib.GObservations`
381 Observation container
382 """
383 # Return container
384 return self._obs
385
386 def pntdef(self, csv):
387 """
388 Set pointing definition from a CSV table
389
390 Parameters
391 ----------
392 csv : `~gammalib.GCsv`
393 Comma-separated values table
394 """
395 # Set pointing definition
396 self._pntdef = csv
397
398 # Return
399 return
400
401
402# ======================== #
403# Main routine entry point #
404# ======================== #
405if __name__ == '__main__':
406
407 # Create instance of application
408 app = csobsdef(sys.argv)
409
410 # Execute application
411 app.execute()
_set_irf(self, obs, caldb, irf)
Definition csobsdef.py:119