ctools  2.0.0
 All Classes Namespaces Files Functions Variables Macros Pages
cstsmapsplit.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # ==========================================================================
3 # Create commands to split TS map computation
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 os
22 import sys
23 import math
24 import gammalib
25 import ctools
26 
27 
28 # ================== #
29 # cstsmapsplit class #
30 # ================== #
31 class cstsmapsplit(ctools.cslikelihood):
32  """
33  Generates commands to split TS map computation
34 
35  This class implements the creation of file containing commands to split
36  the computation of a TS map into several machines. It derives from
37  the ctools.cscript class which provides support for parameter files,
38  command line arguments, and logging. In that way the Python script
39  behaves just as a regular ctool.
40  """
41 
42  # Consructor
43  def __init__(self, *argv):
44  """
45  Constructor
46  """
47  # Initialise application by calling the appropriate class constructor
48  self._init_cslikelihood(self.__class__.__name__, ctools.__version__, argv)
49 
50  # Set data members
51  self._outmap = gammalib.GFilename()
52  self._bins_per_job = 0
53  self._compute_null = False
54  self._outfile = gammalib.GFilename()
55  self._map = gammalib.GSkyMap()
56  self._cmd = []
57  self._srcname = ''
58 
59  # Return
60  return
61 
62 
63  # Private methods
64  def _get_parameters(self):
65  """
66  Get parameters from parfile and setup the observation
67  """
68  # If there are no observations in container then get some ...
69  if self.obs().size() == 0:
70  self.obs(self._get_observations())
71 
72  # Set models if we have none
73  if self.obs().models().size() == 0:
74  self.obs().models(self['inmodel'].filename())
75 
76  # Get TS map paramters
77  self._map = self._create_map(self.obs())
78  self._srcname = self['srcname'].string()
79  self._outmap = self['outmap'].filename()
80 
81  # Get additional parameters
82  self._bins_per_job = self['bins_per_job'].integer()
83  self._compute_null = self['compute_null'].boolean()
84  self._outfile = self['outfile'].filename()
85 
86  # Write input parameters into logger
87  self._log_parameters(gammalib.TERSE)
88 
89  # Return
90  return
91 
93  """
94  Compute null hypothesis
95 
96  Returns
97  -------
98  logl : float
99  Log-likelihood of null hypothesis
100  """
101  # Store original models
102  models_orig = self.obs().models()
103 
104  # Get models instance
105  models0 = self.obs().models()
106 
107  # Remove test source
108  models0.remove(self._srcname)
109 
110  # Set models for null hypothesis
111  self.obs().models(models0)
112 
113  # Optimise observation container
114  self.obs().optimize(self.opt())
115 
116  # Set original models to container
117  self.obs().models(models_orig)
118 
119  # Return optimised log-likelihood of null hypothesis
120  return -(self.opt().value())
121 
122 
123  # Public methods
124  def process(self):
125  """
126  Process the script
127  """
128  # Get parameters
129  self._get_parameters()
130 
131  # Write information into logger
132  self._log_header1(gammalib.TERSE, 'Test source')
133  self._log_string(gammalib.TERSE, str(self.obs().models()[self._srcname]))
134 
135  # Set log-likelihood to zero
136  logL0 = 0.0
137 
138  # Pre-compute null hypothesis if requested
139  if self._compute_null:
140 
141  # Write information into logger
142  self._log_header1(gammalib.TERSE, 'Compute null hypothesis')
143 
144  # Compute null hypothesis
145  logL0 = self._compute_null_hypothesis()
146 
147  # Write likelihood into logger
148  self._log_value(gammalib.TERSE, 'Source removed', self._srcname)
149  self._log_value(gammalib.TERSE, 'Log-likelihood', repr(logL0))
150 
151  # Get parameters of TS map ctool
152  pars = gammalib.GApplicationPars('cttsmap.par')
153 
154  # Compute total number of jobs
155  nbins = self._map.npix()
156  njobs = int(math.ceil(float(nbins) / float(self._bins_per_job)) + 0.1)
157 
158  # Set parameters to be skipped now, we will deal with them later
159  skip_pars = ['binmin', 'binmax', 'logL0', 'outmap', 'logfile']
160 
161  # Set tool name for computation
162  base_command = 'cttsmap'
163 
164  # Write information into logger
165  self._log_header1(gammalib.TERSE, 'Create commands')
166  self._log_value(gammalib.TERSE, 'Number of cttsmap calls', njobs)
167 
168  # Loop over TS map parameters
169  for par in pars:
170 
171  # Skip if we deal with them later
172  if par.name() in skip_pars:
173  continue
174 
175  # Skip if parameter was not queried
176  if not self[par.name()].was_queried():
177  continue
178 
179  # Set TS map parameter according to input from
180  # this script
181  par.value(self[par.name()].value())
182 
183  # Append command to set parameter
184  base_command += ' ' + par.name() + '=' + par.value()
185 
186  # Append null hypothesis parameter
187  base_command += ' logL0=' + repr(logL0)
188 
189  # Set binning to start from zero
190  binmin = 0
191  binmax = 0
192 
193  # Clear command sequence
194  self._cmd = []
195 
196  # Loop over jobs and create commands
197  for job in range(njobs):
198 
199  # Set specific outmap file name
200  outmap = self._outmap.url().replace('.fits', '_'+str(job)+'.fits')
201 
202  # Set bin numbers to be computed in this job
203  binmin = binmax
204  binmax = binmin + self._bins_per_job
205  if binmax > nbins:
206  binmax = nbins
207 
208  # Setup sliced command
209  sliced_command = '%s binmin=%s binmax=%s outmap=%s logfile=%s' % \
210  (base_command, str(binmin), str(binmax),
211  outmap, outmap.replace('.fits','.log'))
212 
213  # If running in background is requested then append a &
214  if self['run_in_bkg'].boolean():
215  sliced_command += ' &'
216 
217  # Append command to list of commands
218  self._cmd.append(sliced_command)
219 
220  # Write information into logger
221  if self._logExplicit():
222  self._log('\n')
223  self._log.header2('Commands')
224  for cmd in self._cmd:
225  self._log(cmd)
226  self._log('\n')
227  self._log('\n')
228 
229  # Return
230  return
231 
232  def save(self):
233  """
234  Save commands to ASCII file
235  """
236  # Get filename
237  filename = self._outfile.url()
238 
239  # Open file
240  f = open(filename, 'w')
241 
242  # Write commands to file
243  for cmd in self._cmd:
244  f.write(cmd + '\n')
245 
246  # Add wait statement
247  f.write('wait\n')
248 
249  # Close file
250  f.close()
251 
252  # Make file executable
253  os.system('chmod +x %s' % filename)
254 
255  # Return
256  return
257 
258 
259 # ======================== #
260 # Main routine entry point #
261 # ======================== #
262 if __name__ == '__main__':
263 
264  # Create instance of application
265  app = cstsmapsplit(sys.argv)
266 
267  # Execute application
268  app.execute()