ctools  2.0.0
 All Classes Namespaces Files Functions Variables Macros Pages
csresmap.py
Go to the documentation of this file.
1 #! /usr/bin/env python
2 # ==========================================================================
3 # Generates a residual map.
4 #
5 # Copyright (C) 2014-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 gammalib
23 import ctools
24 from cscripts import obsutils
25 
26 
27 # ============== #
28 # csresmap class #
29 # ============== #
30 class csresmap(ctools.csobservation):
31  """
32  Generates a residual map
33  """
34 
35  # Constructor
36  def __init__(self, *argv):
37  """
38  Constructor
39  """
40  # Initialise application by calling the appropriate class constructor
41  self._init_csobservation(self.__class__.__name__, ctools.__version__, argv)
42 
43  # Initialise class members
44  self._resmap = None
45  self._use_maps = False
46  self._skip_binning = False
47 
48  # Return
49  return
50 
51 
52  # Private methods
53  def _get_parameters(self):
54  """
55  Get parameters from parfile and setup the observation
56  """
57  # Initialise some flags
58  self._use_maps = False
59  self._skip_binning = False
60 
61  # If there are no observations in the observation container then check
62  # if the "inobs" parameter is a counts cube. If the "inobs" parameter
63  # represents a FITS file and if this FITS file is a counts cube then
64  # set self._skip_binning=True
65  if self.obs().size() == 0 and self['inobs'].filename() != 'NONE':
66  filename = gammalib.GFilename(self['inobs'].filename())
67  if filename.is_fits():
68  cta = gammalib.GCTAObservation()
69  cta.load(filename)
70  if cta.eventtype() == 'CountsCube':
71  self._skip_binning = True
72 
73  # If we have a counts cube, then ask whether we also have a model cube.
74  # If a model cube name is given then set self._use_maps=True
75  if self._skip_binning:
76  self._use_maps = self['modcube'].is_valid()
77 
78  # If not two maps are given, proceed to set up observation
79  if not self._use_maps:
80 
81  # Set observation if not done before
82  if self.obs().size() == 0:
83  self._require_inobs('csresmap.get_parameters()')
84  self.obs(self._get_observations())
85 
86  # If we have exactly one binned CTA observation then signal that
87  # the binning can be skipped
88  if self.obs().size() == 1 and \
89  self.obs()[0].classname() == 'GCTAObservation' and \
90  self.obs()[0].eventtype() == 'CountsCube':
91  self._skip_binning = True
92 
93  # Set models if we have none
94  if self.obs().models().size() == 0:
95  self.obs().models(self['inmodel'].filename())
96 
97  # If no binning is provided in the observation then query now
98  # the counts cube binning parameters
99  if not self._skip_binning:
100  self['xref'].real()
101  self['yref'].real()
102  self['coordsys'].string()
103  self['proj'].string()
104  self['nxpix'].integer()
105  self['nypix'].integer()
106  self['binsz'].real()
107  self['ebinalg'].string()
108  if self['ebinalg'].string() == 'FILE':
109  self['ebinfile'].filename()
110  else:
111  self['emin'].real()
112  self['emax'].real()
113  self['enumbins'].integer()
114  if self['ebinalg'].string() == 'POW':
115  self['ebingamma'].real()
116 
117  # Query parameters
118  self['edisp'].boolean()
119  self['algorithm'].string()
120  self['publish'].boolean()
121  self['chatter'].integer()
122  self['clobber'].boolean()
123  self['debug'].boolean()
124 
125  # Query ahead output parameters
126  if (self._read_ahead()):
127  self['outmap'].filename()
128 
129  # Write input parameters into logger
130  self._log_parameters(gammalib.TERSE)
131 
132  # Return
133  return
134 
135 
136  # Public methods
137  def process(self):
138  """
139  Process the script
140  """
141  # Get parameters
142  self._get_parameters()
143 
144  # Write observation into logger
145  self._log_observations(gammalib.NORMAL, self.obs(), 'Observation')
146 
147  # If a counts and model cube are specified then load them as sky map
148  if self._use_maps:
149  countmap = gammalib.GSkyMap(self['inobs'].filename())
150  modelmap = gammalib.GSkyMap(self['modcube'].filename())
151 
152  # ... otherwise build a counts cube and model cube
153  else:
154 
155  # Do not build counts cube if we have already one in the observation
156  # container
157  if self._skip_binning:
158  cta_counts_cube = gammalib.GCTAEventCube(self.obs()[0].events())
159 
160  # ... otherwise generate one now from the event list
161  else:
162 
163  # Write header
164  self._log_header1(gammalib.TERSE, 'Generate binned map (ctbin)')
165 
166  # Create counts cube
167  cta_counts_cube = obsutils.create_counts_cube(self, self.obs())
168 
169  # Assign GCTAEventCube to skymap
170  countmap = cta_counts_cube.counts()
171 
172  # Write header
173  self._log_header1(gammalib.TERSE, 'Generate model map (ctmodel)')
174 
175  # Create model map
176  model = ctools.ctmodel(self.obs())
177  model.cube(cta_counts_cube)
178  model['chatter'] = self['chatter'].integer()
179  model['clobber'] = self['clobber'].boolean()
180  model['debug'] = self['debug'].boolean()
181  model['edisp'] = self['edisp'].boolean()
182  model.run()
183 
184  # Get model map into GSkyMap object
185  modelmap = model.cube().counts().copy()
186 
187  # Calculate residual maps
188  # Note that we need a special
189  # construct here to avoid memory leaks. This seems to be a SWIG feature
190  # as SWIG creates a new object when calling binning.cube()
191  countmap1 = countmap.copy()
192  countmap1.stack_maps()
193  modelmap.stack_maps()
194  self._resmap = obsutils.residuals(self,countmap1,modelmap)
195 
196  # Optionally publish map
197  if self['publish'].boolean():
198  self.publish()
199 
200  # Return
201  return
202 
203  def save(self):
204  """
205  Save residual map
206  """
207  # Write header
208  self._log_header1(gammalib.TERSE, 'Save residual map')
209 
210  # Get outmap parameter
211  outmap = self['outmap'].filename()
212 
213  # Continue only filename and residual map are valid
214  if self._resmap != None:
215 
216  # Log file name
217  self._log_value(gammalib.TERSE, 'Residual map file', outmap.url())
218 
219  # Save residual map
220  self._resmap.save(outmap, self['clobber'].boolean())
221 
222  # Stamp residual map
223  self._stamp(outmap)
224 
225  # Return
226  return
227 
228  def publish(self, name=''):
229  """
230  Publish residual map
231 
232  Parameters
233  ----------
234  name : str, optional
235  Name of residual map
236  """
237  # Write header
238  self._log_header1(gammalib.TERSE, 'Publish residual map')
239 
240  # Continue only if residual map is valid
241  if self._resmap != None:
242 
243  # Set default name is user name is empty
244  if not name:
245  user_name = self._name()
246  else:
247  user_name = name
248 
249  # Log map name
250  self._log_value(gammalib.TERSE, 'Residual map name', user_name)
251 
252  # Publish map
253  self._resmap.publish(user_name)
254 
255  # Return
256  return
257 
258  def models(self, models):
259  """
260  Set model
261  """
262  # Copy models
263  self.obs().models(models)
264 
265  # Return
266  return
267 
268  def resmap(self):
269  """
270  Return residual map
271 
272  Returns
273  -------
274  map : `~gammalib.GSkyMap'
275  Residual sky map
276  """
277  # Return
278  return self._resmap
279 
280 
281 # ======================== #
282 # Main routine entry point #
283 # ======================== #
284 if __name__ == '__main__':
285 
286  # Create instance of application
287  app = csresmap(sys.argv)
288 
289  # Execute application
290  app.execute()