ctools  2.0.0
 All Classes Namespaces Files Functions Variables Macros Pages
csmodelselect.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # ==========================================================================
3 # Selects model from a model 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 # csmodelselect class #
28 # =================== #
29 class csmodelselect(ctools.csobservation):
30  """
31  Selects model from a model 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  # Set name
42 
43  # Initialise class members
44  self._models = gammalib.GModels()
45 
46  # Return
47  return
48 
49 
50  # Private methods
51  def _get_parameters(self):
52  """
53  Get parameters from parfile and setup the observation
54  """
55  # Query input parameters
56  self['inobs'].filename()
57  self['inmodel'].filename()
58 
59  # Query hidden parameters
60  self['roilimit'].real()
61  self['roimargin'].real()
62  self['ethres'].real()
63  self['fluxlimit'].real()
64  self['tslimit'].real()
65  self['fit_pos'].boolean()
66  self['fit_shape'].boolean()
67 
68  # Query ahead output model filename
69  if self._read_ahead():
70  self['outmodel'].filename()
71 
72  # If there are no observations in container then get them from the
73  # parameter file
74  if self.obs().size() == 0:
75  self.obs(self._get_observations(False))
76 
77  # Get models
78  self._models = gammalib.GModels(self['inmodel'].filename())
79 
80  # Write input parameters into logger
81  self._log_parameters(gammalib.TERSE)
82 
83  # Return
84  return
85 
86  def _select_model(self, model, obs):
87  """
88  Select model
89 
90  Parameters
91  ----------
92  model : `~gammalib.GModel`
93  Model
94  obs : `~gammalib.GObservations`
95  Observation container
96 
97  Returns
98  -------
99  select : bool
100  Model selection flag
101  """
102  # Get selection parameters
103  roilimit = self['roilimit'].real()
104  roimargin = self['roimargin'].real()
105  ethres = self['ethres'].real()
106  fluxlimit = self['fluxlimit'].real()
107  tslimit = self['tslimit'].real()
108 
109  # Initialise selection flag to True
110  select = True
111  msg = 'Select by default'
112 
113  # If the model has a spatial component then check of overlap
114  if hasattr(model, 'spatial'):
115 
116  # Determine the model bounding box
117  model_bounds = model.spatial().region()
118 
119  # Initialise with an unselect model
120  select = False
121  msg = 'Exclude since outside any RoI'
122 
123  # Loop over all observations and check whether the model falls
124  # into one of them
125  for o in obs:
126 
127  # Get RoI centre and radius. The RoI radius is limited by
128  # roilimit. A margin given by roimargin is added to the RoI
129  # radius.
130  #obs_centre = o.roi().centre().dir() # Segmentation fault
131  #obs_radius = o.roi().radius() # Segmentation fault
132  obs_roi = o.roi()
133  obs_centre = obs_roi.centre().dir()
134  obs_radius = obs_roi.radius()
135  if obs_radius > roilimit:
136  obs_radius = roilimit
137  obs_radius += roimargin
138 
139  # Set circular sky region
140  obs_bounds = gammalib.GSkyRegionCircle(obs_centre, obs_radius)
141 
142  # If model overlaps with RoI then signal overlap
143  if obs_bounds.overlaps(model_bounds):
144  select = True
145  msg = 'Select due to overlap with at least one RoI'
146  break
147 
148  # If model is selected and if model has a TS value then apply TS
149  # selection
150  if select and model.has_ts():
151  if model.ts() < tslimit:
152  select = False
153  msg = 'Exclude since below TS limit (TS=%.3f)' % model.ts()
154 
155  # If model is selected and if model is a sky model then apply flux
156  # limit selection
157  if select and model.classname() == 'GModelSky':
158  emin = gammalib.GEnergy(ethres, 'TeV')
159  emax = gammalib.GEnergy(1000.0, 'TeV')
160  flux = model.spectral().flux(emin, emax)
161  if flux < fluxlimit:
162  select = False
163  msg = 'Exclude since below flux limit (Flux=%.3e ph/cm2/s)' % flux
164 
165  # Log model selection
166  self._log_value(gammalib.NORMAL, model.name(), msg)
167 
168  # Return selection flag
169  return select
170 
171  def _set_model_parameters(self, model):
172  """
173  Set model parameters
174 
175  Parameters
176  ----------
177  model : `~gammalib.GModel`
178  Model
179 
180  Returns
181  -------
182  model : `~gammalib.GModel`
183  Model with parameter set
184  """
185  # Get selection parameters
186  fit_pos = self['fit_pos'].boolean()
187  fit_shape = self['fit_shape'].boolean()
188 
189  # If the model has a spatial component then set the spatial parameters
190  if hasattr(model, 'spatial'):
191 
192  # Loop over all parameters
193  for par in model:
194 
195  # Handle position parameters
196  if par.name() == 'RA' or par.name() == 'DEC':
197  if fit_pos:
198  par.free()
199  else:
200  par.fix()
201 
202  # Handle shape parameters
203  elif par.name() == 'Radius' or \
204  par.name() == 'Sigma' or \
205  par.name() == 'Width' or \
206  par.name() == 'PA' or \
207  par.name() == 'MajorRadius' or \
208  par.name() == 'MinorRadius':
209  if fit_shape:
210  par.free()
211  else:
212  par.fix()
213 
214  # Return model
215  return model
216 
217 
218  # Public methods
219  def process(self):
220  """
221  Process the script
222  """
223  # Get parameters
224  self._get_parameters()
225 
226  # Initialise empty model container for selected models
227  models = gammalib.GModels()
228 
229  # Write input observation container into logger
230  self._log_observations(gammalib.NORMAL, self.obs(), 'Input observation')
231 
232  # Write input models into logger
233  self._log_models(gammalib.NORMAL, self._models, 'Input model')
234 
235  # Write header
236  self._log_header1(gammalib.NORMAL, 'Model selection')
237 
238  # Loop over model components
239  for model in self._models:
240 
241  # If model should be selected then set model parameters and append
242  # model to the output container
243  if self._select_model(model, self.obs()):
244 
245  # Set model parameters
246  model = self._set_model_parameters(model)
247 
248  # Append model
249  models.append(model)
250 
251  # Copy selected model into selected models
252  self._models = models
253 
254  # Write selected models into logger
255  self._log_models(gammalib.NORMAL, self._models, 'Selected model')
256 
257  # Return
258  return
259 
260  def save(self):
261  """
262  Save model definition XML file
263  """
264  # Write header
265  self._log_header1(gammalib.TERSE, 'Save models')
266 
267  # Get output filename in case it was not read ahead
268  outmodel = self['outmodel'].filename()
269 
270  # If file exists and clobber flag is false then raise an exception
271  if outmodel.exists() and not self._clobber:
272  msg = ('Cannot save "'+outmodel.url()+'": File already exists. '
273  'Use parameter clobber=yes to allow overwriting of files.')
274  raise RuntimeError(msg)
275 
276  # Otherwise log filename and save file
277  else:
278  # Log filename
279  self._log_value(gammalib.NORMAL, 'Model definition XML file',
280  outmodel.url())
281 
282  # Save models
283  self._models.save(outmodel)
284 
285  # Return
286  return
287 
288 
289 # ======================== #
290 # Main routine entry point #
291 # ======================== #
292 if __name__ == '__main__':
293 
294  # Create instance of application
295  app = csmodelselect(sys.argv)
296 
297  # Execute application
298  app.execute()