ctools 2.1.0.dev
Loading...
Searching...
No Matches
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# ==========================================================================
21import sys
22import gammalib
23import ctools
24
25
26# =================== #
27# csmodelselect class #
28# =================== #
29class 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# ======================== #
292if __name__ == '__main__':
293
294 # Create instance of application
295 app = csmodelselect(sys.argv)
296
297 # Execute application
298 app.execute()