Commit 0be0a1a4450dd64c86fc9b87dd08a291684a8716

Authored by Jürgen Knödlseder
1 parent 90c2e5d8

Integrate code

ChangeLog
1 -2018-11-23 1 +2018-12-11
2 2
3 * Version 1.6.0 released 3 * Version 1.6.0 released
4 ======================== 4 ========================
@@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
7 CTA background rates are now per ontime (#2758) 7 CTA background rates are now per ontime (#2758)
8 Apply event bin weighting in ctmodel for stacked computation (#2721) 8 Apply event bin weighting in ctmodel for stacked computation (#2721)
9 Enable ctbin to produce counts cubes for a joint binned analysis (#2714) 9 Enable ctbin to produce counts cubes for a joint binned analysis (#2714)
  10 + Add ctfindvar tool (#2713)
10 Add Jupyter notebooks for H.E.S.S. DR1 (#2712) 11 Add Jupyter notebooks for H.E.S.S. DR1 (#2712)
11 csphagen now creates appropriate model for likelihood fitting (#2711) 12 csphagen now creates appropriate model for likelihood fitting (#2711)
12 csphagen now supports any kind of background model (#2710) 13 csphagen now supports any kind of background model (#2710)
1 New Features and Important Changes in ctools 1.6.0 1 New Features and Important Changes in ctools 1.6.0
2 2
3 -23 November 2018 3 +11 December 2018
4 4
5 5
6 Introduction 6 Introduction
@@ -126,6 +126,12 @@ ctexpcube - CTA exposure cube generation @@ -126,6 +126,12 @@ ctexpcube - CTA exposure cube generation
126 None 126 None
127 127
128 128
  129 +ctfindvar - Variability search
  130 +------------------------------
  131 +New: the ctfindvar tool searches for source variability using an On/Off method
  132 +in the time domain (#2713).
  133 +
  134 +
129 ctlike - Maximum likelihood fitting 135 ctlike - Maximum likelihood fitting
130 ----------------------------------- 136 -----------------------------------
131 Add hidden "like_accuracy" parameter to allow control of the accuracy of 137 Add hidden "like_accuracy" parameter to allow control of the accuracy of
doc/source/admin/release_history/1.6.rst
@@ -14,6 +14,7 @@ In particular, this release provides: @@ -14,6 +14,7 @@ In particular, this release provides:
14 * Support for Xspec spectral analysis 14 * Support for Xspec spectral analysis
15 * Improved computation speed for :ref:`ctskymap` and :ref:`csphagen` 15 * Improved computation speed for :ref:`ctskymap` and :ref:`csphagen`
16 * Parallelisation of various cscripts 16 * Parallelisation of various cscripts
  17 +* Support for source variability searches
17 * Support for Python pickeling 18 * Support for Python pickeling
18 * A Code Generator to facilitate adding ctools and cscripts 19 * A Code Generator to facilitate adding ctools and cscripts
19 20
@@ -102,6 +103,8 @@ Improvements @@ -102,6 +103,8 @@ Improvements
102 New features 103 New features
103 ------------ 104 ------------
104 105
  106 +* [`2713 <https://cta-redmine.irap.omp.eu/issues/2713>`_] -
  107 + Add :ref:`ctfindvar` tool
105 * [`2709 <https://cta-redmine.irap.omp.eu/issues/2709>`_] - 108 * [`2709 <https://cta-redmine.irap.omp.eu/issues/2709>`_] -
106 Add code generator 109 Add code generator
107 * [`2694 <https://cta-redmine.irap.omp.eu/issues/2694>`_] - 110 * [`2694 <https://cta-redmine.irap.omp.eu/issues/2694>`_] -
doc/source/users/reference_manual/ctfindvar.rst
1 .. _ctfindvar: 1 .. _ctfindvar:
2 2
3 ctfindvar 3 ctfindvar
4 -============================== 4 +=========
5 5
6 -ToDo: Describe in a one liner what the tool is doing. 6 +Search for source variability.
7 7
8 8
9 Synopsis 9 Synopsis
10 -------- 10 --------
11 -This tool is used to search for source variability.  
12 -The variability is computed by binning the arrival time of the photons  
13 -and applying the on-off method to each bin, integrating the background  
14 -over all the other bins.  
15 -A Limit is set to include a bin in the background sample, i.e., its  
16 -significance with respect to all the others bins has to be lower than 4.5  
17 -The significance of each bin is computed using Li&Ma eq. 17.  
18 -A limit on the background sample can also be included, taking only into  
19 -account bins that have a number of counts which is higher than a threshold  
20 -defined by the user via the "minoff" parameter.  
21 -  
22 -Variability is calculated for each pixel of the sky given in the input files.  
23 -The results of the variability is further stored in the srcsig.fits file,  
24 -where the GTIs and the significance of each bin are stored.  
25 -Only the sources with a significance > 4.5 sigmas are stored.  
26 -  
27 -:ref:`ctfindvar` takes in input some observation files.  
28 -It calculates a cube with several maps, each one with a count number integrated  
29 -over a time period defined by the tinterval parameter, for each pixel.  
30 -The tinterval parameter is the time scale over which the variability  
31 -will be searched.  
32 -  
33 -Inputs files:  
34 -- observation file xml, or event list .fits  
35 -  
36 -Output files:  
37 -- countscube.fits  
38 - provides all the count-skymaps contained in the cube.  
39 -- peaksigmap.fits  
40 - a skymap of the highest significance for each pixel  
41 -- srcsig.fits  
42 - a table containing the GTIs together with the significance  
43 - for all the sources with a significant variability detected.  
44 -  
45 -  
46 -The significance of a source can be displayed using the  
47 -``show_variability_evolution.py`` script in the example forlder 11 +
  12 +This tool searches for source variability by applying the On/Off method in the
  13 +time domain. The variability of sky map pixels is computed by binning the events
  14 +as function of the photon arrival time and applying the On/Off method to each
  15 +time bin, integrating the background over all the other time bins. Only bins
  16 +with a variabilty significance below a given threshold, defined by the hidden
  17 +``threshold`` parameter, are included in the background estimate. Furthermore,
  18 +only time bins that exceed a minimum number of events, specified by the hidden
  19 +``minoff`` parameter, are considered for the background estimate. The significance
  20 +of each time bin is computed using Li & Ma formula.
  21 +
  22 +:ref:`ctfindvar` will search a grid of sky map pixels for source variability. Using
  23 +the hidden ``smooth_kernel`` and ``smooth_rad`` parameters, the sky map may be
  24 +smoothed before the variability search. The tool will compute sky maps for a
  25 +number of time bins. The duration of each time bin is specified by the ``tinterval``
  26 +parameter, the start and stop time are defined by the ``tmin`` and ``tmax``
  27 +parameters. If the start and stop time are ``NONE``, the start and stop times will
  28 +be extracted from the input event list(s). Note that ``tinterval`` specifies the
  29 +time scale over which the variability will be searched.
  30 +
  31 +The tool will determine the sky map pixel with the maximum variability and store
  32 +the variability histogram and pixel position as ``MAXSIGPIXEL`` in the output file,
  33 +specified using the ``outmap`` parameter. In addition, the tool will determine the
  34 +variability for a number of source positions. If an input model is specified
  35 +using the ``inmodel`` parameter, the source positions will be defined by the centre
  36 +positions of all gamma-ray source components in the input model. If no input
  37 +model is specified, the tool will use the ``xsrc`` and ``ysrc`` parameters to allow
  38 +the specification of a source position. The results will be stored in the output
  39 +file using either the source names found in the model definition file, or the
  40 +name ``SOURCE`` if the source position was specified by the ``xsrc`` and ``ysrc``
  41 +parameters.
  42 +
  43 +All sky map pixels with a variability above the value specified by the ``threshold``
  44 +will also added be as a point source model to the ``outmodel`` file. Each source will
  45 +have a power law as the spectral component with a total flux that corresponds to
  46 +1% of the Crab nebula flux.
  47 +
48 48
49 General parameters 49 General parameters
50 ------------------ 50 ------------------
@@ -56,7 +56,17 @@ General parameters @@ -56,7 +56,17 @@ General parameters
56 Input model definition file for extracting source positions. 56 Input model definition file for extracting source positions.
57 57
58 ``(outcube = NONE) [file]`` 58 ``(outcube = NONE) [file]``
59 - Filename for saving counts cube, NONE will result in no cube being saved. 59 + Filename for saving counts cube, ``NONE`` will result in no cube being saved.
  60 +
  61 +``outmap [file]``
  62 + Filename for output sky map containing the maximum significance for each
  63 + spatial pixel. The file also contains the variability significance as function
  64 + of time for each source of interest, and the pixel with the maximum
  65 + significance. Furthermore, it contains the Right Ascension and Declination
  66 + of each source of interest and the pixel with the maximum significance.
  67 +
  68 +``outmodel [file]``
  69 + Output model definition file containing sources for significant pixels.
60 70
61 ``caldb [string]`` 71 ``caldb [string]``
62 Calibration database (only required for IRF background subtraction if no 72 Calibration database (only required for IRF background subtraction if no
@@ -66,40 +76,45 @@ General parameters @@ -66,40 +76,45 @@ General parameters
66 Instrument response function (only required for IRF background subtraction 76 Instrument response function (only required for IRF background subtraction
67 if no response information is provided by ``inobs``). 77 if no response information is provided by ``inobs``).
68 78
69 -``prefix [string]``  
70 - Output file prefix. The method will save two files:  
71 - - Skymap containing the maximum significance for each spatial pixel  
72 - - File containing the significance as a function of time interval for each  
73 - source  
74 79
75 -``(minoff = 0) [real]``  
76 - Minimum number of off counts to compute the significance 80 +Variability search parameters
  81 +-----------------------------
  82 +
  83 +``coordsys <CEL|GAL> [string]``
  84 + Coordinate system (CEL - celestial, GAL - galactic).
  85 +
  86 +``xsrc [real]``
  87 + Right Ascension / Galactic longitude of source of interest (J2000, in degrees)
  88 +
  89 +``ysrc [real]``
  90 + Declination / Galactic latitude of source of interest (J2000, in degrees)
77 91
78 ``emin [real]`` 92 ``emin [real]``
79 - Minimum energy (TeV) for extracting events. A value of 0 results in no cut  
80 - being applied. 93 + Minimum energy (TeV) for extracting events.
81 94
82 ``emax [real]`` 95 ``emax [real]``
83 - Maximum energy (TeV) for extracting events. A value of 0 results in no cut  
84 - being applied. 96 + Maximum energy (TeV) for extracting events.
85 97
86 -``(sig_thr = 4.5) [real]`` 98 +``(threshold = 4.5) [real]``
87 Significance threshold for variability detection. 99 Significance threshold for variability detection.
88 100
89 -``(smoothkrnl = NONE) <GAUSSIAN|DISK|NONE> [string]`` 101 +``(minoff = 0) [real]``
  102 + Minimum number of off counts to compute the significance.
  103 +
  104 +``(smooth_kernel = NONE) <GAUSSIAN|DISK|NONE> [string]``
90 Kernel to be used in smoothing the counts maps before computing 105 Kernel to be used in smoothing the counts maps before computing
91 significances. 106 significances.
92 107
93 -``(smoothpar = 0.05) [real]``  
94 - Parameter for the smoothing kernel. This is either the sigma of the GAUSSIAN  
95 - or the radius of the DISK. 108 +``(smooth_rad = 0.05) [real]``
  109 + Radius of smoothing kernel in degrees. This is either the sigma of the
  110 + ``GAUSSIAN`` or the radius of the ``DISK``.
96 111
97 112
98 Time binning parameters 113 Time binning parameters
99 ----------------------- 114 -----------------------
100 115
101 ``tinterval [real]`` 116 ``tinterval [real]``
102 - interval in seconds for each time step. 117 + Interval in seconds for each time step.
103 118
104 ``tmin [time]`` 119 ``tmin [time]``
105 Start time for interval determination (UTC string, JD, MJD or MET in seconds). 120 Start time for interval determination (UTC string, JD, MJD or MET in seconds).
@@ -120,7 +135,7 @@ Spatial binning parameters @@ -120,7 +135,7 @@ Spatial binning parameters
120 -------------------------- 135 --------------------------
121 136
122 ``(usepnt = no) [boolean]`` 137 ``(usepnt = no) [boolean]``
123 - Use CTA pointing direction for cube centre instead of xref/yref parameters? 138 + Use CTA pointing direction for sky map centre instead of xref/yref parameters?
124 139
125 ``nxpix [integer]`` 140 ``nxpix [integer]``
126 Number of cube bins in Right Ascension or Galactic longitude. 141 Number of cube bins in Right Ascension or Galactic longitude.
@@ -129,25 +144,16 @@ Spatial binning parameters @@ -129,25 +144,16 @@ Spatial binning parameters
129 Number of cube bins in Declination or Galactic latitude. 144 Number of cube bins in Declination or Galactic latitude.
130 145
131 ``binsz [real]`` 146 ``binsz [real]``
132 - Cube bin size (in degrees/pixel).  
133 -  
134 -``coordsys <CEL|GAL> [string]``  
135 - Coordinate system (CEL - celestial, GAL - galactic). 147 + Sky map pixel size (in degrees/pixel).
136 148
137 ``proj <AIT|AZP|CAR|GLS|MER|MOL|SFL|SIN|STG|TAN> [string]`` 149 ``proj <AIT|AZP|CAR|GLS|MER|MOL|SFL|SIN|STG|TAN> [string]``
138 Projection method. 150 Projection method.
139 151
140 ``xref [real]`` 152 ``xref [real]``
141 - Right Ascension / Galactic longitude of cube centre (J2000, in degrees). 153 + Right Ascension / Galactic longitude of sky map centre (J2000, in degrees).
142 154
143 ``yref [real]`` 155 ``yref [real]``
144 - Declination / Galactic latitude of cube centre (J2000, in degrees).  
145 -  
146 -``xsrc [real]``  
147 - Right Ascension / Galactic longitude of source of interest (J2000, in degrees)  
148 -  
149 -``ysrc [real]``  
150 - Declination / Galactic latitude of source of interest (J2000, in degrees) 156 + Declination / Galactic latitude of sky map centre (J2000, in degrees).
151 157
152 158
153 Standard parameters 159 Standard parameters
doc/source/users/reference_manual/index.rst
@@ -17,7 +17,6 @@ ctools @@ -17,7 +17,6 @@ ctools
17 .. toctree:: 17 .. toctree::
18 :maxdepth: 1 18 :maxdepth: 1
19 19
20 - ctfindvar --- ToDo: Describe what tool is doing <ctfindvar>  
21 ctbin --- Generates counts cube <ctbin> 20 ctbin --- Generates counts cube <ctbin>
22 ctbkgcube --- Generates background cube <ctbkgcube> 21 ctbkgcube --- Generates background cube <ctbkgcube>
23 ctbutterfly --- Compute butterfly <ctbutterfly> 22 ctbutterfly --- Compute butterfly <ctbutterfly>
@@ -25,6 +24,7 @@ ctools @@ -25,6 +24,7 @@ ctools
25 ctedispcube --- Generates energy dispersion cube <ctedispcube> 24 ctedispcube --- Generates energy dispersion cube <ctedispcube>
26 cterror --- Calculates likelihood profile errors <cterror> 25 cterror --- Calculates likelihood profile errors <cterror>
27 ctexpcube --- Generates exposure cube <ctexpcube> 26 ctexpcube --- Generates exposure cube <ctexpcube>
  27 + ctfindvar --- Search for source variability <ctfindvar>
28 ctlike --- Performs maximum likelihood fitting <ctlike> 28 ctlike --- Performs maximum likelihood fitting <ctlike>
29 ctmapcube --- Generates a map cube <ctmapcube> 29 ctmapcube --- Generates a map cube <ctmapcube>
30 ctmodel --- Computes model counts cube <ctmodel> 30 ctmodel --- Computes model counts cube <ctmodel>
examples/show_variability_evolution.py
1 #! /usr/bin/env python 1 #! /usr/bin/env python
2 # ========================================================================== 2 # ==========================================================================
3 -# Shows butterfly diagram created with ctbutterfly 3 +# Shows source variability evolution created with ctfindvar
4 # 4 #
5 -# Copyright (C) 2014-2017 Simon Bonnefoy 5 +# Copyright (C) 2018 Simon Bonnefoy
6 # 6 #
7 # This program is free software: you can redistribute it and/or modify 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 8 # it under the terms of the GNU General Public License as published by
@@ -43,6 +43,8 @@ def plot_variability_evolution(filename, plotfile, srcname): @@ -43,6 +43,8 @@ def plot_variability_evolution(filename, plotfile, srcname):
43 Name of spectrum FITS file 43 Name of spectrum FITS file
44 plotfile : str 44 plotfile : str
45 Plot file name 45 Plot file name
  46 + srcname : str
  47 + Source name
46 """ 48 """
47 # Read variability file 49 # Read variability file
48 fits = gammalib.GFits(filename) 50 fits = gammalib.GFits(filename)
@@ -51,41 +53,45 @@ def plot_variability_evolution(filename, plotfile, srcname): @@ -51,41 +53,45 @@ def plot_variability_evolution(filename, plotfile, srcname):
51 c_gti_stop = table['TSTOP'] 53 c_gti_stop = table['TSTOP']
52 c_sig = table[srcname] 54 c_sig = table[srcname]
53 55
54 - #convert the c_sig object to list  
55 - table_c_sig=[]  
56 - table_c_gti=[]  
57 - 56 + # Convert the c_sig object to list
  57 + table_c_sig = []
  58 + table_c_gti = []
58 for k in c_sig: 59 for k in c_sig:
59 table_c_sig.append(k) 60 table_c_sig.append(k)
60 - for j,k in zip(c_gti_start, c_gti_stop): 61 + for j, k in zip(c_gti_start, c_gti_stop):
61 table_c_gti.append(0.5*(j+k)) 62 table_c_gti.append(0.5*(j+k))
62 63
63 - #Initializing the plots  
64 - axarr=[]  
65 - f = plt.figure(figsize=(12,7))  
66 - ax1 = f.add_subplot(121)  
67 - ax2 = f.add_subplot(122) 64 + # Initializing the plots
  65 + axarr = []
  66 + f = plt.figure(figsize=(12,7))
  67 + ax1 = f.add_subplot(121)
  68 + ax2 = f.add_subplot(122)
68 axarr.append(ax1) 69 axarr.append(ax1)
69 axarr.append(ax2) 70 axarr.append(ax2)
70 71
  72 + # Create first plot
71 axarr[0].plot(table_c_gti, table_c_sig) 73 axarr[0].plot(table_c_gti, table_c_sig)
72 axarr[0].set_title('Significance evolution') 74 axarr[0].set_title('Significance evolution')
73 axarr[0].set_xlabel('MJD') 75 axarr[0].set_xlabel('MJD')
74 axarr[0].set_ylabel('Significance') 76 axarr[0].set_ylabel('Significance')
75 77
76 - axarr[1].hist(table_c_sig, 20, range=[min(table_c_sig), max(table_c_sig)], histtype='stepfilled', facecolor='g', alpha=0.75) 78 + # Create second plot
  79 + axarr[1].hist(table_c_sig, 20, range=[min(table_c_sig), max(table_c_sig)],
  80 + histtype='stepfilled', facecolor='g', alpha=0.75)
77 axarr[1].set_title('Significance distribution') 81 axarr[1].set_title('Significance distribution')
78 axarr[1].set_xlabel('Significance') 82 axarr[1].set_xlabel('Significance')
79 axarr[1].set_ylabel('Counts') 83 axarr[1].set_ylabel('Counts')
80 84
81 - #IT SEEMS THAT THE LOG SCALE IS NOT WORKING WELL !!!!!!  
82 - #axarr[1].gca().set_yscale('log')  
83 - 85 + # Optionally save result in file
84 if plotfile != "": 86 if plotfile != "":
85 f.savefig(plotfile+".png") 87 f.savefig(plotfile+".png")
86 88
87 plt.show() 89 plt.show()
88 90
  91 + # Return
  92 + return
  93 +
  94 +
89 # ========================== # 95 # ========================== #
90 # Show variability evolution # 96 # Show variability evolution #
91 # ========================== # 97 # ========================== #
src/ctfindvar/ctfindvar.cpp
@@ -39,6 +39,7 @@ @@ -39,6 +39,7 @@
39 #endif 39 #endif
40 40
41 /* __ Method name definitions ____________________________________________ */ 41 /* __ Method name definitions ____________________________________________ */
  42 +#define G_INX2GTI "ctfindvar::inx2gti(int&)"
42 #define G_FILL_CUBE "ctfindvar::fill_cube(GCTAObservation*)" 43 #define G_FILL_CUBE "ctfindvar::fill_cube(GCTAObservation*)"
43 #define G_FILL_ALPHA_VECTOR "ctfindvar::fill_alpha_vector(const int&, "\ 44 #define G_FILL_ALPHA_VECTOR "ctfindvar::fill_alpha_vector(const int&, "\
44 "vector<double>&)" 45 "vector<double>&)"
@@ -235,146 +236,16 @@ void ctfindvar::run(void) @@ -235,146 +236,16 @@ void ctfindvar::run(void)
235 // Write input observation container into logger 236 // Write input observation container into logger
236 log_observations(NORMAL, m_obs, "Input observation"); 237 log_observations(NORMAL, m_obs, "Input observation");
237 238
238 - // Loop over all unbinned CTA observations in the container  
239 - #pragma omp parallel for  
240 - for (int i = 0; i < m_obs.size(); ++i) {  
241 -  
242 - // Get pointer to observation  
243 - GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);  
244 -  
245 - // Fill the cube  
246 - fill_cube(obs);  
247 -  
248 - // Dispose events to free memory  
249 - obs->dispose_events();  
250 -  
251 - } // endfor: looped over observations  
252 -  
253 - // Smooth the maps if requested  
254 - if ((*this)["smoothkrnl"].is_valid() &&  
255 - (*this)["smoothkrnl"].string() != "NONE") {  
256 - m_counts.smooth((*this)["smoothkrnl"].string(),  
257 - (*this)["smoothpar"].real());  
258 - }  
259 -  
260 - // Get number of maps  
261 - const int nbins = m_counts.nmaps();  
262 -  
263 - // creating GSkyDir to get the position of the source and each pixels  
264 -  
265 - // Extract which pixel in the map each source is located in  
266 - std::vector<int> srcInxPix;  
267 - if (m_inmodel.size() > 0) {  
268 - for (int mod = 0; mod < m_inmodel.size(); ++mod) {  
269 -  
270 - // Extract the model  
271 - GModelSky* model = dynamic_cast<GModelSky*>(m_inmodel[mod]);  
272 - if (model != NULL) {  
273 - // Get the source position  
274 - GModelSpatial* model_spatial = model->spatial();  
275 - GSkyRegion* model_region = model_spatial->region();  
276 - GSkyDir srcSkyDir =  
277 - dynamic_cast<GSkyRegionCircle*>(model_region)->centre();  
278 -  
279 - // Store the map index of this source  
280 - int src_map_index = m_counts.dir2inx(srcSkyDir);  
281 - srcInxPix.push_back(src_map_index);  
282 - } else {  
283 - m_inmodel.remove(mod);  
284 - mod--;  
285 - }  
286 - }  
287 - }  
288 - // ... otherwise only one source position is queried  
289 - else {  
290 - GSkyDir srcSkyDir;  
291 - if ((*this)["coordsys"].string() == "CEL") {  
292 - srcSkyDir.radec_deg((*this)["xsrc"].real(),(*this)["ysrc"].real());  
293 - } else {  
294 - srcSkyDir.lb_deg((*this)["xsrc"].real(),(*this)["ysrc"].real());  
295 - }  
296 -  
297 - // Store the index of this source  
298 - int src_map_index = m_counts.dir2inx(srcSkyDir);  
299 - srcInxPix.push_back(src_map_index);  
300 - }  
301 -  
302 - //preparing the histogram with significance evolution  
303 - //for the source center and the highest sig-pix  
304 - m_pixsigsrc = GNdarray(srcInxPix.size(), nbins);  
305 - m_pixsigmax = GNdarray(nbins);  
306 -  
307 - //Prepare the final skymap with the max significance of each pixel  
308 - double max_sig = 0;  
309 -  
310 - log_header1(NORMAL, "Looping Over Pixels");  
311 -  
312 - //looping over all the pixels in the cube  
313 - #pragma omp parallel for  
314 - for (int pix_number = 0; pix_number < m_counts.npix(); pix_number++) {  
315 -  
316 - //Preparing array to store significance evolution of  
317 - //each pixel  
318 - GNdarray pixSig(nbins);  
319 - double total_counts=0;  
320 -  
321 - //Checking the integrated count of each pixel  
322 - for (int k = 0; k < m_counts.nmaps(); k++) {  
323 - total_counts += m_counts(pix_number, k);  
324 - }  
325 -  
326 - //Getting the variability significance for the pixel  
327 - get_variability_sig(pix_number,nbins, pixSig);  
328 -  
329 - //Getting the significance evolution for the source  
330 - for (int src = 0; src < srcInxPix.size(); src++) { 239 + // Create GTIs
  240 + init_gtis();
331 241
332 - // Store the distribution if the source is located at this position  
333 - if (srcInxPix[src] == pix_number) { 242 + // Create counts cube
  243 + create_cube();
334 244
335 - #pragma omp critical(ctfind_run)  
336 - for (int i = 0; i < nbins; i++) {  
337 - m_pixsigsrc(src,i) = pixSig(i);  
338 - }  
339 - }  
340 - #ifdef G_DEBUG  
341 - std::cout << "checking pixel number of the source of interest" << pix_number << std::endl;  
342 - std::cout << "number of counts in pixel of the source of interest" << total_counts << std::endl;  
343 - pixSigSrc=pixSig;  
344 - std::cout << "checking pixel number " << pix_number << " with total number of counts of: " << total_counts << std::endl;  
345 - std::cin.ignore();  
346 - std::cout << "checking pixel number of the source of interest: " << pix_number << " - with total number of counts of: " << total_counts << std::endl;  
347 - #endif  
348 - }  
349 - }  
350 -  
351 - // Getting the evolution for the pix with highest significance  
352 - #pragma omp critical(ctfind_run)  
353 - {  
354 - if (max(pixSig) > max_sig) {  
355 - // Update the pixel position with the maximum sigma  
356 - max_sig = max(pixSig);  
357 - m_max_sig_dir = m_counts.inx2dir(pix_number);  
358 - m_pixsigmax = pixSig;  
359 - }  
360 - }  
361 - //storing sig in skymap  
362 - m_peaksigmap(pix_number) = max(pixSig);  
363 -  
364 - //If the significance is greater than sig threshold, the position  
365 - //is stored in a model  
366 - if (max(pixSig) > m_sig_threshold) {  
367 - GSkyDir dir_pix = m_counts.inx2dir(pix_number);  
368 - fill_model_sig_pos(dir_pix);  
369 - }  
370 -  
371 - }  
372 -  
373 - log_header1(NORMAL, "Analysis finished");  
374 - log_value(NORMAL, "Maximum sigma", max_sig);  
375 - log_value(NORMAL, "Max pixel RA", m_max_sig_dir.ra_deg());  
376 - log_value(NORMAL, "Max pixel DEC", m_max_sig_dir.dec_deg()); 245 + // Analyse cube
  246 + analyse_cube();
377 247
  248 + // Return
378 return; 249 return;
379 } 250 }
380 251
@@ -388,37 +259,52 @@ void ctfindvar::run(void) @@ -388,37 +259,52 @@ void ctfindvar::run(void)
388 ***************************************************************************/ 259 ***************************************************************************/
389 void ctfindvar::save(void) 260 void ctfindvar::save(void)
390 { 261 {
  262 + // Write header
391 log_header1(TERSE, "Saving results"); 263 log_header1(TERSE, "Saving results");
392 264
393 - // Filenames  
394 - std::string prefix((*this)["prefix"].string());  
395 -  
396 // Write counts cube 265 // Write counts cube
397 if ((*this)["outcube"].is_valid()) { 266 if ((*this)["outcube"].is_valid()) {
  267 +
  268 + // Get counts cube output filename
398 GFilename outcube = (*this)["outcube"].filename(); 269 GFilename outcube = (*this)["outcube"].filename();
399 - log_value(TERSE, "Saving counts cube", outcube); 270 +
  271 + // Save counts cube
400 m_counts.save(outcube, (*this)["clobber"].boolean()); 272 m_counts.save(outcube, (*this)["clobber"].boolean());
  273 +
  274 + // Log saving
  275 + log_value(TERSE, "Saving counts cube", outcube);
401 } 276 }
402 277
403 // Create a FITS file for storing the output 278 // Create a FITS file for storing the output
404 - m_outfile.clear(); 279 + GFits fits;
405 280
406 // Write the most significant values for each pixel 281 // Write the most significant values for each pixel
407 - log_string(TERSE, "Appending maximum significances map to output file");  
408 - m_peaksigmap.write(m_outfile, "PEAKSIGMAP"); 282 + m_peaksigmap.write(fits, "PEAKSIGMAP");
  283 +
  284 + // Write source histograms
  285 + write_source_histograms(fits);
409 286
410 - // Write individual source distributions  
411 - write_srchist(); 287 + // Get output map filename
  288 + GFilename outmap = (*this)["outmap"].filename();
412 289
413 - // Write the output file  
414 - GFilename outfilename = prefix + "signifmap.fits";  
415 - log_value(TERSE, "Saving results to ", outfilename);  
416 - m_outfile.saveto(outfilename, (*this)["clobber"].boolean()); 290 + // Save output map
  291 + fits.saveto(outmap, (*this)["clobber"].boolean());
417 292
418 - //Save the model file with position with significance 293 + // Log saving
  294 + log_value(TERSE, "Saving output map", outmap);
  295 +
  296 + // Save the model definition file
419 if (m_model_above_thr.size() > 2) { 297 if (m_model_above_thr.size() > 2) {
420 - GFilename model_filename = prefix + "model_significant_directions.xml";  
421 - m_model_above_thr.save(model_filename); 298 +
  299 + // Get model output filename
  300 + GFilename outmodel = (*this)["outmodel"].filename();
  301 +
  302 + // Save model
  303 + m_model_above_thr.save(outmodel);
  304 +
  305 + // Log saving
  306 + log_value(TERSE, "Saving output model", outmodel);
  307 +
422 } 308 }
423 309
424 // Return 310 // Return
@@ -429,7 +315,7 @@ void ctfindvar::save(void) @@ -429,7 +315,7 @@ void ctfindvar::save(void)
429 /***********************************************************************//** 315 /***********************************************************************//**
430 * @brief Get the map index associated with a given time 316 * @brief Get the map index associated with a given time
431 * 317 *
432 - * @param[in] time Time 318 + * @param[in] time Time
433 * @return Index of map in counts cube 319 * @return Index of map in counts cube
434 ***************************************************************************/ 320 ***************************************************************************/
435 int ctfindvar::time2inx(const GTime& time) 321 int ctfindvar::time2inx(const GTime& time)
@@ -438,29 +324,35 @@ int ctfindvar::time2inx(const GTime&amp; time) @@ -438,29 +324,35 @@ int ctfindvar::time2inx(const GTime&amp; time)
438 int map_index = -1; 324 int map_index = -1;
439 325
440 // Loop over all GTIs 326 // Loop over all GTIs
441 - for (int i = 0; i < m_gti.size(); i++) {  
442 - // Check if interval contains the time 327 + for (int i = 0; i < m_gti.size(); ++i) {
  328 +
  329 + // If interval contains the time then break
443 if (m_gti[i].contains(time)) { 330 if (m_gti[i].contains(time)) {
444 map_index = i; 331 map_index = i;
445 break; 332 break;
446 } 333 }
  334 +
447 } 335 }
448 336
  337 + // Return map index
449 return map_index; 338 return map_index;
450 } 339 }
451 340
452 341
453 /***********************************************************************//** 342 /***********************************************************************//**
454 - * @brief Return reference to significance cube object 343 + * @brief Return Good Time Interval for index
  344 + *
  345 + * @param[in] index Index
  346 + * @return Good Time Interval
455 ***************************************************************************/ 347 ***************************************************************************/
456 GGti ctfindvar::inx2gti(const int& index) 348 GGti ctfindvar::inx2gti(const int& index)
457 { 349 {
  350 + // Throw an exception if index is invalid
458 if ((index < 0) || (index >= m_gti.size())) { 351 if ((index < 0) || (index >= m_gti.size())) {
459 - // Index is invalid, so throw an error  
460 - throw GException::invalid_value("ctfindvar::inx2gti(const int&)",  
461 - "'index' parameter out of range"); 352 + throw GException::out_of_range(G_INX2GTI, "Time index", index, m_gti.size());
462 } 353 }
463 354
  355 + // Return Good Time Interval
464 return m_gti[index]; 356 return m_gti[index];
465 } 357 }
466 358
@@ -483,7 +375,6 @@ void ctfindvar::init_members(void) @@ -483,7 +375,6 @@ void ctfindvar::init_members(void)
483 m_max_sig_dir.clear(); 375 m_max_sig_dir.clear();
484 m_minoff = 0.0; 376 m_minoff = 0.0;
485 m_sig_threshold = 0.0; 377 m_sig_threshold = 0.0;
486 - m_outfile.clear();  
487 m_peaksigmap.clear(); 378 m_peaksigmap.clear();
488 m_pixsigsrc.clear(); 379 m_pixsigsrc.clear();
489 m_tstart.clear(); 380 m_tstart.clear();
@@ -511,7 +402,6 @@ void ctfindvar::copy_members(const ctfindvar&amp; app) @@ -511,7 +402,6 @@ void ctfindvar::copy_members(const ctfindvar&amp; app)
511 m_max_sig_dir = app.m_max_sig_dir; 402 m_max_sig_dir = app.m_max_sig_dir;
512 m_minoff = app.m_minoff; 403 m_minoff = app.m_minoff;
513 m_sig_threshold = app.m_sig_threshold; 404 m_sig_threshold = app.m_sig_threshold;
514 - m_outfile = app.m_outfile;  
515 m_peaksigmap = app.m_peaksigmap; 405 m_peaksigmap = app.m_peaksigmap;
516 m_pixsigsrc = app.m_pixsigsrc; 406 m_pixsigsrc = app.m_pixsigsrc;
517 m_tstart = app.m_tstart; 407 m_tstart = app.m_tstart;
@@ -537,39 +427,35 @@ void ctfindvar::free_members(void) @@ -537,39 +427,35 @@ void ctfindvar::free_members(void)
537 427
538 /***********************************************************************//** 428 /***********************************************************************//**
539 * @brief Get application parameters 429 * @brief Get application parameters
540 - *  
541 - * @todo Implement method  
542 ***************************************************************************/ 430 ***************************************************************************/
543 void ctfindvar::get_parameters(void) 431 void ctfindvar::get_parameters(void)
544 { 432 {
545 - #ifdef _OPENMP  
546 - int nthreads = (*this)["nthreads"].integer();  
547 - if (nthreads > 0) {  
548 - omp_set_num_threads(nthreads);  
549 - }  
550 - #endif  
551 -  
552 // Load the observations 433 // Load the observations
553 setup_observations(m_obs, true, true, false); 434 setup_observations(m_obs, true, true, false);
554 435
555 - // Get the rest of the parameters  
556 - if (read_ahead()) {  
557 - (*this)["prefix"].string();  
558 -  
559 - if ((*this)["inmodel"].is_valid()) {  
560 - m_inmodel = GModels((*this)["inmodel"].filename());  
561 - } else {  
562 - // Get the desired source position from user  
563 - (*this)["xsrc"].real();  
564 - (*this)["ysrc"].real();  
565 - } 436 + // Either load model or query source position parameters
  437 + if ((*this)["inmodel"].is_valid()) {
  438 + m_inmodel = GModels((*this)["inmodel"].filename());
  439 + }
  440 + else {
  441 + (*this)["coordsys"].string();
  442 + (*this)["xsrc"].real();
  443 + (*this)["ysrc"].real();
566 } 444 }
567 445
568 - // Create GTIs and counts cube  
569 - init_gtis();  
570 - init_cube(); 446 + // Query time limit parameters
  447 + (*this)["tinterval"].real();
  448 + if ((*this)["tmin"].is_valid()) {
  449 + (*this)["tmin"].time();
  450 + }
  451 + if ((*this)["tmax"].is_valid()) {
  452 + (*this)["tmax"].time();
  453 + }
  454 +
  455 + // Create map to query spatial parameters
  456 + create_map(m_obs);
571 457
572 - // Get the energy limits 458 + // Read energy limits
573 m_emin = GEnergy((*this)["emin"].real(), "TeV"); 459 m_emin = GEnergy((*this)["emin"].real(), "TeV");
574 m_emax = GEnergy((*this)["emax"].real(), "TeV"); 460 m_emax = GEnergy((*this)["emax"].real(), "TeV");
575 461
@@ -577,7 +463,28 @@ void ctfindvar::get_parameters(void) @@ -577,7 +463,28 @@ void ctfindvar::get_parameters(void)
577 m_minoff = (*this)["minoff"].real(); 463 m_minoff = (*this)["minoff"].real();
578 464
579 // Get minimum significance to set a source as variable 465 // Get minimum significance to set a source as variable
580 - m_sig_threshold = (*this)["sig_thr"].real(); 466 + m_sig_threshold = (*this)["threshold"].real();
  467 +
  468 + // Query smoothing parameters
  469 + if ((*this)["smooth_kernel"].is_valid()) {
  470 + (*this)["smooth_kernel"].string();
  471 + (*this)["smooth_rad"].real();
  472 + }
  473 +
  474 + // If needed later, query output filenames now
  475 + if (read_ahead()) {
  476 + (*this)["outcube"].query();
  477 + (*this)["outmap"].query();
  478 + (*this)["outmodel"].query();
  479 + }
  480 +
  481 + // Set number of OpenMP threads
  482 + #ifdef _OPENMP
  483 + int nthreads = (*this)["nthreads"].integer();
  484 + if (nthreads > 0) {
  485 + omp_set_num_threads(nthreads);
  486 + }
  487 + #endif
581 488
582 // Write parameters into logger 489 // Write parameters into logger
583 log_parameters(TERSE); 490 log_parameters(TERSE);
@@ -588,117 +495,139 @@ void ctfindvar::get_parameters(void) @@ -588,117 +495,139 @@ void ctfindvar::get_parameters(void)
588 495
589 496
590 /***********************************************************************//** 497 /***********************************************************************//**
591 - * @brief Initialize counts cube 498 + * @brief Initialize Good Time Intervals
592 * 499 *
593 - * Sets up the required event cube and time interval storage objects  
594 - * associated with this object  
595 - ***************************************************************************/  
596 -void ctfindvar::init_cube(void)  
597 -{  
598 - // Create the basic skymap  
599 - m_counts = create_map(m_obs);  
600 -  
601 - // Resize to the appropriate number of time intervals  
602 - m_counts.nmaps(m_gti.size());  
603 -  
604 - // Create the peaksigmap  
605 - m_peaksigmap = m_counts.extract(1);  
606 -}  
607 -  
608 -  
609 -/***********************************************************************//**  
610 - * @brief Initialize counts cube 500 + * @exception GException::invalid_value
  501 + * Invalid start or stop time or insufficient time bins
611 * 502 *
612 - * Sets up the required event cube and time interval storage objects  
613 - * associated with this object 503 + * Initialise Good Time Intervals for the variability search.
614 ***************************************************************************/ 504 ***************************************************************************/
615 void ctfindvar::init_gtis(void) 505 void ctfindvar::init_gtis(void)
616 { 506 {
617 - double tinterval = (*this)["tinterval"].real();  
618 - m_tstart = GTime("JD 999999999"); // large unreasonable Julian date  
619 - m_tstop = GTime("JD 0"); // small unreasonable Julian date 507 + // Log header
  508 + log_header1(NORMAL, "Initialise Good Time Intervals");
620 509
621 - // Set the start time  
622 - if ((*this)["tmin"].is_valid()) {  
623 - log_string(NORMAL, "Setting start time from command line");  
624 - m_tstart = (*this)["tmin"].time();  
625 - } else {  
626 - log_string(NORMAL, "Setting start time from observations");  
627 - for (int o=0; o<m_obs.size(); o++) {  
628 - GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[o]);  
629 -  
630 - // Check that the start time of the run is less than tstart  
631 - if (obs->gti().tstart() < m_tstart) {  
632 - m_tstart = obs->gti().tstart();  
633 - }  
634 - }  
635 - } 510 + // Clear time intervals
  511 + m_gti.clear();
636 512
637 - // Set the stop time  
638 - if ((*this)["tmax"].is_valid()) {  
639 - log_string(NORMAL, "Setting stop time from command line");  
640 - m_tstop = (*this)["tmax"].time();  
641 - } else { 513 + // Set start and stop times
  514 + m_tstart = get_tstart();
  515 + m_tstop = get_tstop();
642 516
643 - log_string(NORMAL, "Setting stop time from observations");  
644 - for (int o=0; o<m_obs.size(); o++) {  
645 - GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[o]);  
646 -  
647 - // Check that the stop time of the run is larger than tstop  
648 - if (obs->gti().tstop() > m_tstop) {  
649 - m_tstop = obs->gti().tstop();  
650 - }  
651 - }  
652 - } 517 + // Query time interval
  518 + double tinterval = (*this)["tinterval"].real();
653 519
654 - // Fill the number of good time intervals 520 + // Compute time bins
655 double tstart_sec = m_tstart.secs(); 521 double tstart_sec = m_tstart.secs();
656 double tstop_sec = m_tstop.secs(); 522 double tstop_sec = m_tstop.secs();
657 int bins = (tstop_sec - tstart_sec) / tinterval + 0.5; 523 int bins = (tstop_sec - tstart_sec) / tinterval + 0.5;
658 524
659 // Log information 525 // Log information
660 - log_value(NORMAL, "treference (mjd)", m_tstart.reference().mjdref());  
661 - log_value(NORMAL, "tstart (sec)", tstart_sec);  
662 - log_value(NORMAL, "tstop (sec)", tstop_sec);  
663 - log_value(NORMAL, "Total time", tstop_sec-tstart_sec);  
664 - log_value(NORMAL, "Time interval", tinterval);  
665 - log_value(NORMAL, "nbins", bins);  
666 -  
667 - // Throw if tstart == tstop 526 + log_value(NORMAL, "Reference time (mjd)", m_tstart.reference().mjdref());
  527 + log_value(NORMAL, "Start time (sec)", tstart_sec);
  528 + log_value(NORMAL, "Stop time (sec)", tstop_sec);
  529 + log_value(NORMAL, "Total time (sec)", tstop_sec-tstart_sec);
  530 + log_value(NORMAL, "Time interval (sec)", tinterval);
  531 + log_value(NORMAL, "Number of time bins", bins);
  532 +
  533 + // Throw exception if tstart == tstop
668 if (m_tstart == m_tstop) { 534 if (m_tstart == m_tstop) {
669 throw GException::invalid_value(G_INIT_GTIS, 535 throw GException::invalid_value(G_INIT_GTIS,
670 "Start time is equal to stop time"); 536 "Start time is equal to stop time");
671 - } else if (bins < 2) { 537 + }
  538 + else if (bins < 2) {
672 std::string msg = "Method requires at least two time bins (" + 539 std::string msg = "Method requires at least two time bins (" +
673 gammalib::str(bins) + " bins found)."; 540 gammalib::str(bins) + " bins found).";
674 throw GException::invalid_value(G_INIT_GTIS, msg); 541 throw GException::invalid_value(G_INIT_GTIS, msg);
675 } 542 }
676 543
677 - for (int i=0; i<bins; i++) { 544 + // Set up Good Time Intervals by appending all intervals that overlap
  545 + // with at least one of the observations
  546 + for (int i = 0; i < bins; ++i) {
678 547
679 - // Get the next time interval  
680 - GTime next_tstart(m_tstart + i*tinterval);  
681 - GTime next_tstop(m_tstart + (i+1.0)*tinterval);  
682 - GGti next_gti(next_tstart, next_tstop); 548 + // Get time interval
  549 + GTime tstart(m_tstart + i*tinterval);
  550 + GTime tstop(m_tstart + (i+1.0)*tinterval);
  551 + GGti gti(tstart, tstop);
683 552
684 // Make sure there's an observation that overlaps with this GTI 553 // Make sure there's an observation that overlaps with this GTI
685 - for (int o=0; o<m_obs.size(); o++) {  
686 - GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[o]);  
687 -  
688 - if (gti_overlap(obs->gti(), next_gti) > 0.0) {  
689 - m_gti.push_back(next_gti);  
690 - break;  
691 - }  
692 - } 554 + for (int k = 0; k < m_obs.size(); ++k) {
  555 +
  556 + // Get CTA observation
  557 + GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[k]);
  558 +
  559 + // Continue only if observation is valid
  560 + if (obs != NULL) {
  561 +
  562 + // Add GTI is the time intervals overlap
  563 + if (gti_overlap(obs->gti(), gti) > 0.0) {
  564 + m_gti.push_back(gti);
  565 + break;
  566 + }
  567 +
  568 + } // endif: observation was valid
  569 +
  570 + } // endfor: looped over observations
  571 +
  572 + } // endfor: looped over Good Time Intervals
  573 +
  574 + // Return
  575 + return;
  576 +}
  577 +
  578 +
  579 +/***********************************************************************//**
  580 + * @brief Create counts cube
  581 + *
  582 + * Creates a counts cube comprising a sky map for each Good Time Interval.
  583 + ***************************************************************************/
  584 +void ctfindvar::create_cube(void)
  585 +{
  586 + // Log header
  587 + log_header1(NORMAL, "Create counts cube");
  588 +
  589 + // Create the basic skymap
  590 + m_counts = create_map(m_obs);
  591 +
  592 + // Resize to the appropriate number of time intervals
  593 + m_counts.nmaps(m_gti.size());
  594 +
  595 + // Create the peaksigmap
  596 + m_peaksigmap = m_counts.extract(1);
  597 +
  598 + // Loop over all unbinned CTA observations in the container
  599 + #pragma omp parallel for
  600 + for (int i = 0; i < m_obs.size(); ++i) {
  601 +
  602 + // Get pointer to observation
  603 + GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
  604 +
  605 + // Fill the cube
  606 + fill_cube(obs);
  607 +
  608 + // Dispose events to free memory
  609 + obs->dispose_events();
  610 +
  611 + } // endfor: looped over observations
  612 +
  613 + // Smooth the maps if requested
  614 + if ((*this)["smooth_kernel"].is_valid() &&
  615 + (*this)["smooth_kernel"].string() != "NONE") {
  616 + m_counts.smooth((*this)["smooth_kernel"].string(),
  617 + (*this)["smooth_rad"].real());
693 } 618 }
  619 +
  620 + // Return
  621 + return;
694 } 622 }
695 623
696 624
697 /***********************************************************************//** 625 /***********************************************************************//**
698 - * @brief Fill the cube from the events in the map 626 + * @brief Fill the cube from the events in an observation
699 * 627 *
700 - * Sets up the required event cube and time interval storage objects  
701 - * associated with this object 628 + * @param[in] obs Pointer to CTA observation.
  629 + *
  630 + * Fills the cube from the events in an observation.
702 ***************************************************************************/ 631 ***************************************************************************/
703 void ctfindvar::fill_cube(GCTAObservation* obs) 632 void ctfindvar::fill_cube(GCTAObservation* obs)
704 { 633 {
@@ -733,10 +662,6 @@ void ctfindvar::fill_cube(GCTAObservation* obs) @@ -733,10 +662,6 @@ void ctfindvar::fill_cube(GCTAObservation* obs)
733 int num_outside_time = 0; 662 int num_outside_time = 0;
734 int num_in_map = 0; 663 int num_in_map = 0;
735 664
736 - // Specify whether we are cutting on energy or not  
737 - bool cut_emin = (m_emin.TeV() > 0.0) ? true : false;  
738 - bool cut_emax = (m_emax.TeV() > 0.0) ? true : false;  
739 -  
740 // Fill counts sky map 665 // Fill counts sky map
741 for (int i = 0; i < events->size(); ++i) { 666 for (int i = 0; i < events->size(); ++i) {
742 667
@@ -744,16 +669,14 @@ void ctfindvar::fill_cube(GCTAObservation* obs) @@ -744,16 +669,14 @@ void ctfindvar::fill_cube(GCTAObservation* obs)
744 const GCTAEventAtom* event = (*events)[i]; 669 const GCTAEventAtom* event = (*events)[i];
745 670
746 // Check that the energy is valid 671 // Check that the energy is valid
747 - if ((cut_emin && event->energy() < m_emin) ||  
748 - (cut_emax && event->energy() > m_emax)) { 672 + if ((event->energy() < m_emin) || (event->energy() > m_emax)) {
749 num_outside_ecut++; 673 num_outside_ecut++;
750 continue; 674 continue;
751 } 675 }
752 676
753 - // Determine event time  
754 - GTime evnt_time = event->time();  
755 - int time_indx = time2inx(evnt_time);  
756 - 677 + // Determine event time index
  678 + int time_indx = time2inx(event->time());
  679 +
757 // Check if event is in the GTIs 680 // Check if event is in the GTIs
758 if ((time_indx > -1) && (time_indx < m_counts.nmaps())) { 681 if ((time_indx > -1) && (time_indx < m_counts.nmaps())) {
759 682
@@ -791,10 +714,11 @@ void ctfindvar::fill_cube(GCTAObservation* obs) @@ -791,10 +714,11 @@ void ctfindvar::fill_cube(GCTAObservation* obs)
791 // Increment number of maps 714 // Increment number of maps
792 num_in_map++; 715 num_in_map++;
793 716
794 - } else {  
795 - // Event falls outside the specified time range 717 + }
  718 +
  719 + // ... otherwise, event falls outside the specified time range
  720 + else {
796 num_outside_time++; 721 num_outside_time++;
797 - continue;  
798 } 722 }
799 723
800 } // endfor: looped over all events 724 } // endfor: looped over all events
@@ -818,242 +742,557 @@ void ctfindvar::fill_cube(GCTAObservation* obs) @@ -818,242 +742,557 @@ void ctfindvar::fill_cube(GCTAObservation* obs)
818 742
819 743
820 /***********************************************************************//** 744 /***********************************************************************//**
821 - * @brief Save the current sky direction to model if significance > thr  
822 - *  
823 - * @param[in] dir_pix pixel sky direction  
824 - * @param[out] m_model_sig_above_thr model position with sig>thr 745 + * @brief Analyse all pixels of counts cube
825 ***************************************************************************/ 746 ***************************************************************************/
826 -void ctfindvar::fill_model_sig_pos(const GSkyDir& dir_pix) 747 +void ctfindvar::analyse_cube(void)
827 { 748 {
828 - //Set spatial components  
829 - GModelSpatialPointSource spatial(dir_pix); 749 + // Log header
  750 + log_header1(NORMAL, "Analyse sky map pixels");
830 751
831 - //Set fake spectral component (powerlaw with 1% of Crab)  
832 - GModelSpectralPlaw spectral(5.7e-18, -2.48, GEnergy(0.3, "TeV")); 752 + // Get number of GTIs
  753 + const int nbins = m_gti.size();
833 754
834 - //Creat a sky model  
835 - GModelSky model(spatial, spectral);  
836 -  
837 - //Extracting the coordinate to name the model  
838 - double ra = dir_pix.ra_deg();  
839 - double dec = dir_pix.dec_deg();  
840 - std::string name = "Sky_direction_" + gammalib::str(ra, 4) +  
841 - "_" + gammalib::str(dec, 4);  
842 - model.name(name);  
843 -  
844 - //Add the model for this pixel to the output mode  
845 - m_model_above_thr.append(model); 755 + // Get relevant sky map pixels
  756 + std::vector<int> srcInxPix = get_pixels();
  757 +
  758 + // Preparing the histogram with significance evolution for the source
  759 + // center and the highest sig-pix
  760 + m_pixsigsrc = GNdarray(srcInxPix.size(), nbins);
  761 + m_pixsigmax = GNdarray(nbins);
  762 +
  763 + // Initialise maximum significance
  764 + double max_sig = 0;
  765 +
  766 + // looping over all the pixels in the cube
  767 + #pragma omp parallel for
  768 + for (int ipix = 0; ipix < m_counts.npix(); ++ipix) {
  769 +
  770 + // Compute total counts for pixel
  771 + double total_counts = 0.0;
  772 + for (int k = 0; k < nbins; ++k) {
  773 + total_counts += m_counts(ipix, k);
  774 + }
  775 +
  776 + // Getting the variability significance for the current pixel
  777 + GNdarray pixSig = get_variability_sig(ipix);
  778 +
  779 + // Getting the significance evolution for the source
  780 + for (int src = 0; src < srcInxPix.size(); ++src) {
  781 +
  782 + // Store the distribution if the source is located at this position
  783 + if (srcInxPix[src] == ipix) {
  784 +
  785 + #pragma omp critical(ctfindvar_analyse_cube)
  786 + for (int i = 0; i < nbins; i++) {
  787 + m_pixsigsrc(src,i) = pixSig(i);
  788 + }
  789 + }
  790 +
  791 + } // endfor: looped over sources
  792 +
  793 + // Make sure that this code is not executed in parallel
  794 + #pragma omp critical(ctfindvar_analyse_cube)
  795 + {
  796 + // Determine the pixel with the highest significance
  797 + if (max(pixSig) > max_sig) {
  798 + max_sig = max(pixSig);
  799 + m_max_sig_dir = m_counts.inx2dir(ipix);
  800 + m_pixsigmax = pixSig;
  801 + }
  802 +
  803 + // Storing sig in skymap
  804 + m_peaksigmap(ipix) = max(pixSig);
  805 +
  806 + // If the significance is greater than the significance threshold,
  807 + // the position is stored in a model
  808 + if (max(pixSig) > m_sig_threshold) {
  809 + GSkyDir dir_pix = m_counts.inx2dir(ipix);
  810 + m_model_above_thr.append(sky_model(dir_pix));
  811 + }
  812 + }
846 813
  814 + } // endfor: looped over pixels
  815 +
  816 + // Log results
  817 + log_value(NORMAL, "Maximum significance", max_sig);
  818 + log_value(NORMAL, "Right Ascension of maximum", m_max_sig_dir.ra_deg());
  819 + log_value(NORMAL, "Declination of maximum", m_max_sig_dir.dec_deg());
  820 +
  821 + // Return
  822 + return;
847 } 823 }
  824 +
  825 +
848 /***********************************************************************//** 826 /***********************************************************************//**
849 - * @brief Get the map index associated with a given time 827 + * @brief Return pixel vector
  828 + *
  829 + * @return Vector of sky map pixels
850 * 830 *
851 - * @param[in] pix_number Spatial pixel  
852 - * @param[out] alpha_vector Vector containing effective exposure 831 + * Returns a vector of sky map pixels that corresponds to the source model
  832 + * positions.
853 ***************************************************************************/ 833 ***************************************************************************/
854 -void ctfindvar::fill_alpha_vector(const int& pix_number,  
855 - std::vector<double>& alpha_vector) 834 +std::vector<int> ctfindvar::get_pixels(void)
856 { 835 {
857 - // Pixel position  
858 - GSkyDir pix_dir = m_counts.inx2dir(pix_number); 836 + // Initialise pixel array
  837 + std::vector<int> srcInxPix;
859 838
860 - // Loop over all observations  
861 - for (int i = 0; i < m_obs.size(); i++) {  
862 - // Skip if observation does not overlap with this pixel position  
863 - GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);  
864 - GCTARoi roi = obs->roi();  
865 - GSkyDir roi_centre = roi.centre().dir(); 839 + // If there are input models then extract
  840 + if (m_inmodel.size() > 0) {
866 841
867 - if (roi_centre.dist_deg(pix_dir) > roi.radius()) {  
868 - continue;  
869 - }  
870 -  
871 - // Convert sky direction to instrument direction  
872 - GCTAInstDir instdir = obs->pointing().instdir(pix_dir); 842 + // Loop over models
  843 + for (int i = 0; i < m_inmodel.size(); ++i) {
873 844
874 - // Extract the good time intervals of the observation  
875 - const GGti& obs_gti(obs->gti()); 845 + // Extract the model
  846 + GModelSky* model = dynamic_cast<GModelSky*>(m_inmodel[i]);
876 847
877 - // Loop over all time bins  
878 - for (int j = 0; j < m_gti.size(); j++) { 848 + // If model is a sky model then extract the model centre. Note
  849 + // that this only works for a model that results a GSkyRegionCircle
  850 + // object as region.
  851 + if (model != NULL) {
879 852
880 - // Make sure observation overlaps with this time interval  
881 - double exposure = gti_overlap(m_gti[j], obs_gti);  
882 - if (exposure > 0.0) { 853 + // Get the source position
  854 + GModelSpatial* spatial = model->spatial();
  855 + GSkyRegion* region = spatial->region();
  856 + GSkyRegionCircle* circle = dynamic_cast<GSkyRegionCircle*>(region);
  857 +
  858 + // If region circle is valid then
  859 + if (circle != NULL) {
  860 + int ipix = m_counts.dir2inx(circle->centre());
  861 + srcInxPix.push_back(ipix);
  862 + continue;
  863 + }
883 864
884 - // Get IRF response  
885 - const GCTAResponseIrf* rsp = dynamic_cast<const GCTAResponseIrf*>  
886 - (obs->response()); 865 + } // endif: model was a sky model
887 866
888 - // Throw an exception if observation has no instrument response function  
889 - if (rsp == NULL) {  
890 - std::string msg =  
891 - "No response information available for "+  
892 - get_obs_header(obs)+" to compute IRF background. "  
893 - "Please specify response information or use "  
894 - "another background subtraction method.";  
895 - throw GException::invalid_value(G_FILL_ALPHA_VECTOR, msg);  
896 - } 867 + // If we are still alive then remove the model since it is neither
  868 + // a sky model, nor does it have a region circle
  869 + m_inmodel.remove(i);
  870 + i--;
897 871
898 - // Get IRF background template  
899 - const GCTABackground* bkg = rsp->background(); 872 + } // endfor: looped over all models
900 873
901 - // Throw an exception if observation has no IRF background template  
902 - if (bkg == NULL) {  
903 - std::string msg =  
904 - "No IRF background template found in instrument "  
905 - "response function for "+  
906 - get_obs_header(obs)+". Please specify an instrument "  
907 - "response function containing a background template.";  
908 - throw GException::invalid_value(G_FILL_ALPHA_VECTOR, msg);  
909 - } 874 + } // endif: there were input models
  875 +
  876 + // ... otherwise query the source position
  877 + else {
910 878
911 - // Multiply background rate with livetime and solid angle  
912 - #pragma omp critical(ctfindvar_alpha_vector)  
913 - alpha_vector[j] += exposure * bkg->rate_ebin(instdir, m_emin, m_emax);  
914 - } 879 + // Query source direction
  880 + GSkyDir dir;
  881 + if ((*this)["coordsys"].string() == "CEL") {
  882 + dir.radec_deg((*this)["xsrc"].real(),(*this)["ysrc"].real());
  883 + }
  884 + else {
  885 + dir.lb_deg((*this)["xsrc"].real(),(*this)["ysrc"].real());
915 } 886 }
916 - }  
917 887
918 - return;  
919 -} 888 + // Store the index of this source
  889 + int ipix = m_counts.dir2inx(dir);
  890 + srcInxPix.push_back(ipix);
  891 +
  892 + } // endelse: queried source position
  893 +
  894 + // Return pixel array
  895 + return srcInxPix;
  896 +}
  897 +
  898 +
  899 +/***********************************************************************//**
  900 + * @brief Return sky model for a given sky direction
  901 + *
  902 + * @param[in] dir Sky direction
  903 + * @return Sky model for a given sky direction
  904 + *
  905 + * Returns a point source sky model at the position of the current pixel
  906 + * with a power law spectral model corresponding to 1% of the Crab flux.
  907 + *
  908 + * The model name is built from the celestial coordinates of the source.
  909 + ***************************************************************************/
  910 +GModelSky ctfindvar::sky_model(const GSkyDir& dir) const
  911 +{
  912 + // Set point source spatial component from sky direction
  913 + GModelSpatialPointSource spatial(dir);
920 914
  915 + // Set fake spectral component (powerlaw with 1% of Crab)
  916 + GModelSpectralPlaw spectral(5.7e-18, -2.48, GEnergy(0.3, "TeV"));
921 917
  918 + // Create a sky model
  919 + GModelSky model(spatial, spectral);
  920 +
  921 + // Build model name from celestial coordinates
  922 + double ra = dir.ra_deg();
  923 + double dec = dir.dec_deg();
  924 + std::string name = gammalib::str(ra, 4);
  925 + if (dec < 0.0) {
  926 + name += "-";
  927 + }
  928 + else {
  929 + name += "+";
  930 + }
  931 + name += gammalib::str(std::abs(dec), 4);
  932 +
  933 + // Set model name
  934 + model.name(name);
  935 +
  936 + // Return model
  937 + return model;
  938 +}
922 939
923 940
924 /***********************************************************************//** 941 /***********************************************************************//**
925 - * @brief Obtain the significance of variability in a given bin 942 + * @brief Get the significance of variability for a given skymap pixel
926 * 943 *
927 - * @param[in] pix_number Pixel in the skymap  
928 - * @param[in] nbins Number of bins in the skymap  
929 - * @param[out] sig_histogram Histogram for storing returned significances 944 + * @param[in] ipix Sky map pixel index.
  945 + * @return Histogram of significances.
930 * 946 *
931 * Significance is computed according to Li & Ma equation 17: 947 * Significance is computed according to Li & Ma equation 17:
932 * see: https://doi.org/10.1086/161295 948 * see: https://doi.org/10.1086/161295
933 * Some modification is made in order to handle the case where Non = 0, 949 * Some modification is made in order to handle the case where Non = 0,
934 * but Noff != 0. 950 * but Noff != 0.
935 ***************************************************************************/ 951 ***************************************************************************/
936 -void ctfindvar::get_variability_sig(const int& pix_number,  
937 - const int& nbins,  
938 - GNdarray& sig_histogram) 952 +GNdarray ctfindvar::get_variability_sig(const int& ipix)
939 { 953 {
940 - std::vector<bool> accepted_bin_bckg_vector(nbins, true);  
941 - bool background_validated = false;  
942 - double non, noff;  
943 - double alpha, sig; 954 + // Initialise result
  955 + GNdarray sig_histogram(m_gti.size());
  956 +
  957 + // Initialise vectors
  958 + std::vector<bool> accepted_bin_bckg_vector(m_gti.size(), true);
944 959
945 - // Fill the alpha values for each vector  
946 - std::vector<double> alpha_vector(nbins, 0.0);  
947 - fill_alpha_vector(pix_number, alpha_vector); 960 + // Get alpha values for specified pixel
  961 + std::vector<double> alphas = get_alphas(ipix);
948 962
949 - // Loop over pixels until all background pixls are removed 963 + // Loop over pixels until all background pixels are removed
  964 + bool background_validated = false;
950 while (background_validated == false) { 965 while (background_validated == false) {
  966 +
  967 + // Signal that background was validated
951 background_validated = true; 968 background_validated = true;
952 969
953 // Loop over all the GTIs of the pixel 970 // Loop over all the GTIs of the pixel
954 - for (int i = 0; i < nbins; i++) { 971 + for (int i = 0; i < m_gti.size(); ++i) {
955 972
956 - // The GTI is discared from bckg calculation and not checked again. 973 + // The GTI is discared from background calculation and not
  974 + // checked again
957 if (!accepted_bin_bckg_vector[i]) { 975 if (!accepted_bin_bckg_vector[i]) {
958 continue; 976 continue;
959 - }  
960 - // Check if bin fails minoff check or no observations overlap it  
961 - else if (m_counts(pix_number, i) < m_minoff || alpha_vector[i] == 0) { 977 + }
  978 +
  979 + // ... otherwise check if bin fails minoff check or no observations
  980 + // overlap it
  981 + else if (m_counts(ipix, i) < m_minoff || alphas[i] == 0) {
962 accepted_bin_bckg_vector[i] = false; 982 accepted_bin_bckg_vector[i] = false;
963 continue; 983 continue;
964 } 984 }
965 985
966 - noff = 0.0;  
967 - alpha = 0.0;  
968 - // Gor one GTI selected (i), looping over all the others (j).  
969 - for (int j = 0; j < nbins; j++) { 986 + // ... otherwise
  987 + double noff = 0.0;
  988 + double alpha = 0.0;
  989 +
  990 + // Got one GTI selected (i), looping over all the others (j).
  991 + for (int j = 0; j < m_gti.size(); ++j) {
970 if (j != i && accepted_bin_bckg_vector[j] == 1) { 992 if (j != i && accepted_bin_bckg_vector[j] == 1) {
971 - noff += m_counts(pix_number, j);  
972 - alpha += alpha_vector[j]; 993 + noff += m_counts(ipix, j);
  994 + alpha += alphas[j];
973 } 995 }
974 } 996 }
975 997
976 // The background is averaged on the number of bins -1 998 // The background is averaged on the number of bins -1
977 - non = m_counts(pix_number, i);  
978 - alpha = alpha_vector[i]/alpha; 999 + double non = m_counts(ipix, i);
  1000 + alpha = alphas[i]/alpha;
979 1001
980 // Compute sensitivity in Gaussian sigma using Li & Ma 1002 // Compute sensitivity in Gaussian sigma using Li & Ma
981 double alpha1 = alpha + 1.0; 1003 double alpha1 = alpha + 1.0;
982 double ntotal = non+noff; 1004 double ntotal = non+noff;
983 double arg1 = non/ntotal; 1005 double arg1 = non/ntotal;
984 double arg2 = noff/ntotal; 1006 double arg2 = noff/ntotal;
985 - if (noff == 0.0) {  
986 - sig = 0.0;  
987 - } else if (non > 0.0) {  
988 - double term1 = non * std::log((alpha1/alpha)*arg1);  
989 - double term2 = noff * std::log(alpha1*arg2);  
990 - sig = std::sqrt(2.0 * (term1 + term2));  
991 - } else {  
992 - sig = std::sqrt(2.0 * noff * std::log(alpha1));  
993 - }  
994 -  
995 - // Specify the sign of the significance  
996 - sig *= (non < alpha*noff) ? -1.0 : 1.0;  
997 -  
998 - if (alpha == 0.0) {  
999 - sig = 0.0; 1007 + double sig = 0.0;
  1008 + if (alpha != 0.0) {
  1009 + if (noff == 0.0) {
  1010 + sig = 0.0;
  1011 + }
  1012 + else if (non > 0.0) {
  1013 + double term1 = non * std::log((alpha1/alpha)*arg1);
  1014 + double term2 = noff * std::log(alpha1*arg2);
  1015 + sig = std::sqrt(2.0 * (term1 + term2));
  1016 + }
  1017 + else {
  1018 + sig = std::sqrt(2.0 * noff * std::log(alpha1));
  1019 + }
  1020 + sig *= (non < alpha*noff) ? -1.0 : 1.0;
1000 } 1021 }
1001 1022
1002 // Update the significance 1023 // Update the significance
1003 sig_histogram(i) = sig; 1024 sig_histogram(i) = sig;
1004 1025
1005 - // If bin is significant, it's removed from bckg and we loop again. 1026 + // If bin is significant, it's removed from the background and
  1027 + // we loop again
1006 if (sig > m_sig_threshold) { 1028 if (sig > m_sig_threshold) {
1007 accepted_bin_bckg_vector[i] = false; 1029 accepted_bin_bckg_vector[i] = false;
1008 background_validated = false; 1030 background_validated = false;
1009 } 1031 }
1010 - }  
1011 1032
1012 - } 1033 + } // endfor: looped over all GTIs
  1034 +
  1035 + } // endwhile: iterate until converged
  1036 +
  1037 + // Return significance histogram
  1038 + return sig_histogram;
1013 } 1039 }
1014 1040
1015 1041
1016 /***********************************************************************//** 1042 /***********************************************************************//**
  1043 + * @brief Get alpha vector
  1044 + *
  1045 + * @param[in] ipix Sky map pixel index
  1046 + * @return Vector containing effective exposure
  1047 + ***************************************************************************/
  1048 +std::vector<double> ctfindvar::get_alphas(const int& ipix) const
  1049 +{
  1050 + // Initialise alpha vector
  1051 + std::vector<double> alphas(m_gti.size(), 0.0);
  1052 +
  1053 + // Convert pixel index into sky direction
  1054 + GSkyDir pix_dir = m_counts.inx2dir(ipix);
  1055 +
  1056 + // Loop over all observations
  1057 + for (int i = 0; i < m_obs.size(); ++i) {
  1058 +
  1059 + // Get CTA observation
  1060 + const GCTAObservation* obs = dynamic_cast<const GCTAObservation*>(m_obs[i]);
  1061 +
  1062 + // Fall through if observation is not valid
  1063 + if (obs == NULL) {
  1064 + continue;
  1065 + }
  1066 +
  1067 + // Get RoI. Fall through if observation does not overlap with this
  1068 + // pixel position
  1069 + GCTARoi roi = obs->roi();
  1070 + GSkyDir roi_centre = roi.centre().dir();
  1071 + if (roi_centre.dist_deg(pix_dir) > roi.radius()) {
  1072 + continue;
  1073 + }
  1074 +
  1075 + // Convert sky direction to instrument direction
  1076 + GCTAInstDir instdir = obs->pointing().instdir(pix_dir);
  1077 +
  1078 + // Extract the good time intervals of the observation
  1079 + const GGti& obs_gti(obs->gti());
  1080 +
  1081 + // Loop over all time bins
  1082 + for (int j = 0; j < m_gti.size(); ++j) {
  1083 +
  1084 + // Make sure observation overlaps with this time interval
  1085 + double exposure = gti_overlap(m_gti[j], obs_gti);
  1086 + if (exposure > 0.0) {
  1087 +
  1088 + // Get IRF response
  1089 + const GCTAResponseIrf* rsp =
  1090 + dynamic_cast<const GCTAResponseIrf*>(obs->response());
  1091 +
  1092 + // Throw an exception if observation has no instrument response function
  1093 + if (rsp == NULL) {
  1094 + std::string msg =
  1095 + "No response information available for "+
  1096 + get_obs_header(obs)+" to compute IRF background. "
  1097 + "Please specify response information or use "
  1098 + "another background subtraction method.";
  1099 + throw GException::invalid_value(G_FILL_ALPHA_VECTOR, msg);
  1100 + }
  1101 +
  1102 + // Get IRF background template
  1103 + const GCTABackground* bkg = rsp->background();
  1104 +
  1105 + // Throw an exception if observation has no IRF background template
  1106 + if (bkg == NULL) {
  1107 + std::string msg =
  1108 + "No IRF background template found in instrument "
  1109 + "response function for "+
  1110 + get_obs_header(obs)+". Please specify an instrument "
  1111 + "response function containing a background template.";
  1112 + throw GException::invalid_value(G_FILL_ALPHA_VECTOR, msg);
  1113 + }
  1114 +
  1115 + // Add up background rate
  1116 + alphas[j] += exposure * bkg->rate_ebin(instdir, m_emin, m_emax);
  1117 +
  1118 + } // endif: exposure was positive
  1119 +
  1120 + } // endfor: looped over all time bins
  1121 +
  1122 + } // endfor: looped over all observations
  1123 +
  1124 + // Return alphas
  1125 + return alphas;
  1126 +}
  1127 +
  1128 +
  1129 +/***********************************************************************//**
1017 * @brief Returns number of seconds that two GTIs overlap 1130 * @brief Returns number of seconds that two GTIs overlap
1018 * 1131 *
1019 * @param[in] gti1 First GTI 1132 * @param[in] gti1 First GTI
1020 * @param[in] gti2 Second GTI 1133 * @param[in] gti2 Second GTI
1021 * @return Number of seconds that @p gti1 and @p gti2 overlap 1134 * @return Number of seconds that @p gti1 and @p gti2 overlap
1022 ***************************************************************************/ 1135 ***************************************************************************/
1023 -double ctfindvar::gti_overlap(const GGti& gti1, const GGti& gti2) 1136 +double ctfindvar::gti_overlap(const GGti& gti1, const GGti& gti2) const
1024 { 1137 {
  1138 + // Initialise no overlap
1025 double overlap = 0.0; 1139 double overlap = 0.0;
  1140 +
  1141 + // Set first and second GTI so that gti_1st starts before gti_2nd
1026 const GGti& gti_1st = (gti1.tstart() <= gti2.tstart()) ? gti1 : gti2; 1142 const GGti& gti_1st = (gti1.tstart() <= gti2.tstart()) ? gti1 : gti2;
1027 - const GGti& gti_2nd = (gti1.tstart() > gti2.tstart()) ? gti1 : gti2; 1143 + const GGti& gti_2nd = (gti1.tstart() > gti2.tstart()) ? gti1 : gti2;
1028 1144
1029 - // gti1 starts earlier 1145 + // Compute overlap
1030 if (gti_1st.tstop() > gti_2nd.tstart()) { 1146 if (gti_1st.tstop() > gti_2nd.tstart()) {
1031 if (gti_1st.tstop() <= gti_2nd.tstop()) { 1147 if (gti_1st.tstop() <= gti_2nd.tstop()) {
1032 overlap = gti_1st.tstop() - gti_2nd.tstart(); 1148 overlap = gti_1st.tstop() - gti_2nd.tstart();
1033 - } else if (gti_1st.tstop() > gti_2nd.tstop()) { 1149 + }
  1150 + else if (gti_1st.tstop() > gti_2nd.tstop()) {
1034 overlap = gti_2nd.tstop() - gti_2nd.tstart(); 1151 overlap = gti_2nd.tstop() - gti_2nd.tstart();
1035 } 1152 }
1036 } 1153 }
1037 1154
  1155 + // Return overlap
1038 return overlap; 1156 return overlap;
1039 } 1157 }
1040 1158
1041 1159
1042 /***********************************************************************//** 1160 /***********************************************************************//**
1043 - * @brief Write individual histograms in CSV format 1161 + * @brief Get start time
  1162 + *
  1163 + * @return Start time
  1164 + ***************************************************************************/
  1165 +GTime ctfindvar::get_tstart(void)
  1166 +{
  1167 + // Initialise start time with large unreasonable Julian date
  1168 + GTime tstart("JD 999999999");
  1169 +
  1170 + // If tmin parameter is valid then set the start time
  1171 + if ((*this)["tmin"].is_valid()) {
  1172 +
  1173 + // Query tmin parameter
  1174 + tstart = (*this)["tmin"].time();
  1175 +
  1176 + // Log action
  1177 + log_string(NORMAL, "Setting start time \"tmin\" parameter");
  1178 +
  1179 + } // endif: tmin parameter was valid
  1180 +
  1181 + // ... otherwise set start time from observations
  1182 + else {
  1183 +
  1184 + // Loop over all observations
  1185 + for (int k = 0; k < m_obs.size(); ++k) {
  1186 +
  1187 + // Get CTA observation
  1188 + GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[k]);
  1189 +
  1190 + // Continue only if observation is valid
  1191 + if (obs != NULL) {
  1192 +
  1193 + // Update start time if the start time of the observation is
  1194 + // smaller than the current start time
  1195 + if (obs->gti().tstart() < tstart) {
  1196 + tstart = obs->gti().tstart();
  1197 + }
  1198 +
  1199 + } // endif: observation was valid
  1200 +
  1201 + } // endfor: looped over observations
  1202 +
  1203 + // Log action
  1204 + log_string(NORMAL, "Setting start time from observations");
  1205 +
  1206 + } // endelse: no valid time specified
  1207 +
  1208 + // Return start time
  1209 + return tstart;
  1210 +}
  1211 +
  1212 +
  1213 +/***********************************************************************//**
  1214 + * @brief Get stop time
  1215 + *
  1216 + * @return Stop time
1044 ***************************************************************************/ 1217 ***************************************************************************/
1045 -void ctfindvar::write_srchist(void) 1218 +GTime ctfindvar::get_tstop(void)
  1219 +{
  1220 + // Initialise stop time with small unreasonable Julian date
  1221 + GTime tstop("JD 0");
  1222 +
  1223 + // If tmax parameter is valid then set the stop time
  1224 + if ((*this)["tmax"].is_valid()) {
  1225 +
  1226 + // Query tmax parameter
  1227 + tstop = (*this)["tmax"].time();
  1228 +
  1229 + // Log action
  1230 + log_string(NORMAL, "Setting stop time \"tmax\" parameter");
  1231 +
  1232 + } // endif: tmax parameter was valid
  1233 +
  1234 + // ... otherwise set stop time from observations
  1235 + else {
  1236 +
  1237 + // Loop over all observations
  1238 + for (int k = 0; k < m_obs.size(); ++k) {
  1239 +
  1240 + // Get CTA observation
  1241 + GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[k]);
  1242 +
  1243 + // Continue only if observation is valid
  1244 + if (obs != NULL) {
  1245 +
  1246 + // Update stop time if the stop time of the observation is
  1247 + // larger than the current stop time
  1248 + if (obs->gti().tstop() > tstop) {
  1249 + tstop = obs->gti().tstop();
  1250 + }
  1251 +
  1252 + } // endif: observation was valid
  1253 +
  1254 + } // endfor: looped over observations
  1255 +
  1256 + // Log action
  1257 + log_string(NORMAL, "Setting stop time from observations");
  1258 +
  1259 + } // endelse: no valid time specified
  1260 +
  1261 + // Return stop time
  1262 + return tstop;
  1263 +}
  1264 +
  1265 +
  1266 +/***********************************************************************//**
  1267 + * @brief Write source histograms in FITS format
  1268 + *
  1269 + * @param[in,out] fits FITS file
  1270 + *
  1271 + * This method creates two FITS binary table extensions and appends them to
  1272 + * the FITS file.
  1273 + *
  1274 + * The first extension is named "SOURCE SIGNIFICANCE" and contains the
  1275 + * significance of the variability as function of time bin for the pixel
  1276 + * with the maximum significance and for each source.
  1277 + *
  1278 + * The second extension is named "SOURCE POSITION" and contains the Right
  1279 + * Ascension and Declination for the maximum significance pixel and for each
  1280 + * source.
  1281 + ***************************************************************************/
  1282 +void ctfindvar::write_source_histograms(GFits& fits)
1046 { 1283 {
1047 // Setup data storage for time and source significances 1284 // Setup data storage for time and source significances
1048 double tinterval = (*this)["tinterval"].real(); 1285 double tinterval = (*this)["tinterval"].real();
1049 - int nentries = (m_tstop-m_tstart) / tinterval + 0.5;  
1050 - int nsources = m_pixsigsrc.shape()[0];  
1051 - GNdarray time_info(2, nentries);  
1052 - GNdarray max_pixel_info(nentries);  
1053 - GNdarray src_info(nsources, nentries);  
1054 -  
1055 - // Store the full set of information  
1056 - for (int i=0; i<nentries; i++) { 1286 + int nbins = (m_tstop-m_tstart) / tinterval + 0.5;
  1287 + int nsrc = m_pixsigsrc.shape()[0];
  1288 + GNdarray time_info(2, nbins);
  1289 + GNdarray max_pixel_info(nbins);
  1290 + GNdarray src_info(nsrc, nbins);
  1291 +
  1292 + // Loop over time bins
  1293 + for (int i = 0; i < nbins; ++i) {
  1294 +
  1295 + // Set time bin mid point
1057 GTime tstart_bin(m_tstart + i * tinterval); 1296 GTime tstart_bin(m_tstart + i * tinterval);
1058 GTime tstop_bin(m_tstart + (i+1) * tinterval); 1297 GTime tstop_bin(m_tstart + (i+1) * tinterval);
1059 GTime midpoint((tstop_bin.secs()+tstart_bin.secs())*0.5); 1298 GTime midpoint((tstop_bin.secs()+tstart_bin.secs())*0.5);
@@ -1066,111 +1305,125 @@ void ctfindvar::write_srchist(void) @@ -1066,111 +1305,125 @@ void ctfindvar::write_srchist(void)
1066 int index = time2inx(midpoint); 1305 int index = time2inx(midpoint);
1067 1306
1068 // Loop over every source 1307 // Loop over every source
1069 - for (int src=0; src<nsources; src++) {  
1070 - if (index >= 0) {  
1071 - src_info(src,i) = m_pixsigsrc(src,index);  
1072 - } else {  
1073 - src_info(src,i) = 0.0;  
1074 - } 1308 + for (int k = 0; k < nsrc; ++k) {
  1309 + src_info(k,i) = (index >= 0) ? m_pixsigsrc(k,index) : 0.0;
1075 } 1310 }
1076 1311
1077 - // Fill max pixel information  
1078 - if (index >= 0) {  
1079 - max_pixel_info(i) = m_pixsigmax(index);  
1080 - } 1312 + // Fill maximum pixel information
  1313 + max_pixel_info(i) = (index >= 0) ? m_pixsigmax(index) : 0.0;
  1314 +
  1315 + } // endfor: looped over time bins
  1316 +
  1317 + // Create binary tables
  1318 + GFitsBinTable table_signif(nbins);
  1319 + GFitsBinTable table_pos(nsrc+1);
  1320 + table_signif.extname("SOURCE SIGNIFICANCE");
  1321 + table_pos.extname("SOURCE POSITION");
  1322 +
  1323 + // Create columns for time bins
  1324 + GFitsTableDoubleCol time_start("TSTART", nbins);
  1325 + GFitsTableDoubleCol time_stop("TSTOP", nbins);
  1326 + GFitsTableDoubleCol time_sigma("MAXSIGPIXEL", nbins);
  1327 + time_start.unit("MJD");
  1328 + time_stop.unit("MJD");
  1329 + time_sigma.unit("sigma");
  1330 +
  1331 + // Create columns for source position
  1332 + GFitsTableStringCol pos_name("SOURCE", nsrc+1, 20);
  1333 + GFitsTableDoubleCol pos_ra("RA", nsrc+1);
  1334 + GFitsTableDoubleCol pos_dec("DEC", nsrc+1);
  1335 + pos_ra.unit("deg");
  1336 + pos_dec.unit("deg");
  1337 +
  1338 + // Fill time bin columns
  1339 + for (int i = 0; i < nbins; ++i) {
  1340 + time_start(i) = time_info(0,i);
  1341 + time_stop(i) = time_info(1,i);
  1342 + time_sigma(i) = max_pixel_info(i);
1081 } 1343 }
1082 1344
1083 - // Call the appropriate method for writing the data  
1084 - write_srchist_fits(time_info, max_pixel_info, src_info);  
1085 -} 1345 + // Fill source columns
  1346 + pos_name(0) = "MAXSIGPIXEL";
  1347 + pos_ra(0) = m_max_sig_dir.ra_deg();
  1348 + pos_dec(0) = m_max_sig_dir.dec_deg();
1086 1349
1087 -  
1088 -/***********************************************************************//**  
1089 - * @brief Write individual histograms in FITS format  
1090 - *  
1091 - * @param[in] time_info Start and stop time in MJD for each bin  
1092 - * @param[in] src_info Significance distribution for each source  
1093 -  
1094 - * This method stores the extracted significance for each source at each time  
1095 - * interval between tstart and tstop. Each time bin contains information on  
1096 - * the start and stop time of that bin (in MJD) and the extracted variability  
1097 - * significance of the source in that time bin.  
1098 - ***************************************************************************/  
1099 -void ctfindvar::write_srchist_fits(const GNdarray& time_info,  
1100 - const GNdarray& max_pixel_info,  
1101 - const GNdarray& src_info)  
1102 -{  
1103 - log_string(NORMAL, "Storing histograms in FITS format");  
1104 -  
1105 - // Create the table  
1106 - int nrows = time_info.shape()[1];  
1107 - GFitsBinTable table_signif(nrows);  
1108 - GFitsBinTable table_dir(2);  
1109 - table_signif.extname("SRCSIGNIF");  
1110 - table_dir.extname("SRCPOSITION");  
1111 -  
1112 - // Append the start/stop time and max pixel columns  
1113 - GFitsTableDoubleCol start_mjd("TSTART", nrows);  
1114 - GFitsTableDoubleCol stop_mjd("TSTOP", nrows);  
1115 - GFitsTableDoubleCol max_pix("MAXSIGPIXEL", nrows);  
1116 - for (int i=0; i<nrows; i++) {  
1117 - start_mjd(i) = time_info(0,i);  
1118 - stop_mjd(i) = time_info(1,i);  
1119 - max_pix(i) = max_pixel_info(i);  
1120 - }  
1121 - table_signif.append(start_mjd);  
1122 - table_signif.append(stop_mjd);  
1123 - table_signif.append(max_pix);  
1124 -  
1125 - // Store the position of the peak pixel  
1126 - GFitsTableDoubleCol max_pix_dir("MAXSIGPIXEL",2);  
1127 - max_pix_dir(0) = m_max_sig_dir.ra_deg();  
1128 - max_pix_dir(1) = m_max_sig_dir.dec_deg();  
1129 - table_dir.append(max_pix_dir); 1350 + // Append columns to tables
  1351 + table_signif.append(time_start);
  1352 + table_signif.append(time_stop);
  1353 + table_signif.append(time_sigma);
1130 1354
1131 // Append the distribution for each source 1355 // Append the distribution for each source
1132 - int src_count = src_info.shape()[0];  
1133 - for (int src=0; src<src_count; src++) {  
1134 - 1356 + for (int k = 0; k < nsrc; ++k) {
  1357 +
  1358 + // Initialise source name and direction
1135 std::string src_name("SOURCE"); 1359 std::string src_name("SOURCE");
1136 GSkyDir src_dir; 1360 GSkyDir src_dir;
1137 1361
1138 // Get the name from the input model file 1362 // Get the name from the input model file
1139 - if (m_inmodel.size() > 0) {  
1140 - src_name = m_inmodel[src]->name();  
1141 -  
1142 - // Get source position  
1143 - GModelSky* model = dynamic_cast<GModelSky*>(m_inmodel[src]);  
1144 - GModelSpatial* model_spatial = model->spatial();  
1145 - GSkyRegion* model_region = model_spatial->region();  
1146 - src_dir = dynamic_cast<GSkyRegionCircle*>(model_region)->centre();  
1147 - }  
1148 - // ... otherwise set the position from xsrc,ysrc 1363 + if (m_inmodel.size() > k) {
  1364 +
  1365 + // Extract the model
  1366 + GModelSky* model = dynamic_cast<GModelSky*>(m_inmodel[k]);
  1367 +
  1368 + // If model is a sky model then extract the model centre. Note
  1369 + // that this only works for a model that results a GSkyRegionCircle
  1370 + // object as region.
  1371 + if (model != NULL) {
  1372 +
  1373 + // Get the source position
  1374 + GModelSpatial* spatial = model->spatial();
  1375 + GSkyRegion* region = spatial->region();
  1376 + GSkyRegionCircle* circle = dynamic_cast<GSkyRegionCircle*>(region);
  1377 +
  1378 + // If region circle is valid then extract source name and
  1379 + // sky direction
  1380 + if (circle != NULL) {
  1381 + src_name = m_inmodel[k]->name();
  1382 + src_dir = circle->centre();
  1383 + }
  1384 +
  1385 + } // endif: model was a sky model
  1386 +
  1387 + } // endif: there were enough models in the input model
  1388 +
  1389 + // ... otherwise set the position from the xsrc and ysrc parameters
1149 else { 1390 else {
1150 if ((*this)["coordsys"].string() == "CEL") { 1391 if ((*this)["coordsys"].string() == "CEL") {
1151 - src_dir.radec_deg((*this)["xsrc"].real(), (*this)["yref"].real());  
1152 - } else {  
1153 - src_dir.lb_deg((*this)["xsrc"].real(), (*this)["yref"].real()); 1392 + src_dir.radec_deg((*this)["xsrc"].real(), (*this)["ysrc"].real());
  1393 + }
  1394 + else {
  1395 + src_dir.lb_deg((*this)["xsrc"].real(), (*this)["ysrc"].real());
1154 } 1396 }
1155 } 1397 }
1156 -  
1157 - // Create and fill a new column  
1158 - GFitsTableDoubleCol src_sig_col(src_name, nrows);  
1159 - for (int i=0; i<nrows; i++) {  
1160 - src_sig_col(i) = src_info(src, i); 1398 +
  1399 + // Create new time column for source
  1400 + GFitsTableDoubleCol column(src_name, nbins);
  1401 + column.unit("sigma");
  1402 +
  1403 + // Fill column
  1404 + for (int i = 0; i < nbins; ++i) {
  1405 + column(i) = src_info(k, i);
1161 } 1406 }
1162 - table_signif.append(src_sig_col);  
1163 1407
1164 - // Store the positions of the source  
1165 - GFitsTableDoubleCol src_dir_col(src_name,2);  
1166 - src_dir_col(0) = src_dir.ra_deg();  
1167 - src_dir_col(1) = src_dir.dec_deg();  
1168 - table_dir.append(src_dir_col);  
1169 - } 1408 + // Append column to time table
  1409 + table_signif.append(column);
  1410 +
  1411 + // Store source infirmation
  1412 + pos_name(k+1) = src_name;
  1413 + pos_ra(k+1) = src_dir.ra_deg();
  1414 + pos_dec(k+1) = src_dir.dec_deg();
  1415 +
  1416 + } // endfor: looped over sources
1170 1417
1171 - // Store information regarding the source positions 1418 + // Append columns to tables
  1419 + table_pos.append(pos_name);
  1420 + table_pos.append(pos_ra);
  1421 + table_pos.append(pos_dec);
1172 1422
1173 - // Write the tables to a file  
1174 - m_outfile.append(table_signif);  
1175 - m_outfile.append(table_dir); 1423 + // Append tables to FITS file
  1424 + fits.append(table_signif);
  1425 + fits.append(table_pos);
  1426 +
  1427 + // Return
  1428 + return;
1176 } 1429 }
src/ctfindvar/ctfindvar.hpp
@@ -68,27 +68,22 @@ public: @@ -68,27 +68,22 @@ public:
68 68
69 protected: 69 protected:
70 // Protected methods 70 // Protected methods
71 - void init_members(void);  
72 - void copy_members(const ctfindvar& app);  
73 - void free_members(void);  
74 - void get_parameters(void);  
75 - void init_cube(void);  
76 - void init_gtis(void);  
77 - void fill_cube(GCTAObservation* obs);  
78 - void fill_model_sig_pos(const GSkyDir& dir_pix);  
79 - void fill_alpha_vector(const int& pix_number,  
80 - std::vector<double>& alpha_vector);  
81 - void get_variability_sig(const int& pix_number,  
82 - const int& nbins,  
83 - GNdarray& sig_histogram);  
84 - double gti_overlap(const GGti& gti1,  
85 - const GGti& gti2);  
86 -  
87 - // Writing methods for individual source histograms  
88 - void write_srchist(void);  
89 - void write_srchist_fits(const GNdarray& time_info,  
90 - const GNdarray& max_pixel_info,  
91 - const GNdarray& src_info); 71 + void init_members(void);
  72 + void copy_members(const ctfindvar& app);
  73 + void free_members(void);
  74 + void get_parameters(void);
  75 + void init_gtis(void);
  76 + void create_cube(void);
  77 + void fill_cube(GCTAObservation* obs);
  78 + void analyse_cube(void);
  79 + std::vector<int> get_pixels(void);
  80 + GNdarray get_variability_sig(const int& ipix);
  81 + std::vector<double> get_alphas(const int& ipix) const;
  82 + double gti_overlap(const GGti& gti1, const GGti& gti2) const;
  83 + GTime get_tstart(void);
  84 + GTime get_tstop(void);
  85 + GModelSky sky_model(const GSkyDir& dir) const;
  86 + void write_source_histograms(GFits& fits);
92 87
93 // Protected members 88 // Protected members
94 GSkyMap m_counts; //!< Counts for each time interval 89 GSkyMap m_counts; //!< Counts for each time interval
@@ -97,7 +92,6 @@ protected: @@ -97,7 +92,6 @@ protected:
97 GSkyDir m_max_sig_dir; //!< Sky direction associated with maximum significance 92 GSkyDir m_max_sig_dir; //!< Sky direction associated with maximum significance
98 double m_minoff; //!< Minimum counts for use in significance calculation 93 double m_minoff; //!< Minimum counts for use in significance calculation
99 double m_sig_threshold; //!< Minimum significance required to set source as variable 94 double m_sig_threshold; //!< Minimum significance required to set source as variable
100 - GFits m_outfile; //!< FITS object to store output  
101 GSkyMap m_peaksigmap; //!< Skymap holding the maximum significance 95 GSkyMap m_peaksigmap; //!< Skymap holding the maximum significance
102 GNdarray m_pixsigsrc; //!< Store distributions of the source significances 96 GNdarray m_pixsigsrc; //!< Store distributions of the source significances
103 GNdarray m_pixsigmax; //!< Store distribution for pixel with max significance 97 GNdarray m_pixsigmax; //!< Store distribution for pixel with max significance
src/ctfindvar/ctfindvar.par
@@ -16,22 +16,26 @@ @@ -16,22 +16,26 @@
16 # 16 #
17 # General parameters 17 # General parameters
18 #=================== 18 #===================
19 -inobs, f, a, events.fits,,, "Input event list, counts cube or observation definition XML file" 19 +inobs, f, a, events.fits,,, "Input event list or observation definition XML file"
20 inmodel, f, h, NONE,,, "Input model definition file for extracting source positions" 20 inmodel, f, h, NONE,,, "Input model definition file for extracting source positions"
21 -outcube, f, h, NONE,,, "Filename for counts cube" 21 +outcube, f, h, NONE,,, "Output counts cube file"
  22 +outmap, f, a, map.fits,,, "Output map file"
  23 +outmodel, f, a, model.xml,,, "Output model definition file"
22 caldb, s, a, prod2,,, "Calibration database" 24 caldb, s, a, prod2,,, "Calibration database"
23 irf, s, a, South_0.5h,,, "Instrument response function" 25 irf, s, a, South_0.5h,,, "Instrument response function"
24 -prefix, s, a, findvar_,,, "Prefix for output files"  
25 26
26 # 27 #
27 # Variability search parameters 28 # Variability search parameters
28 #============================== 29 #==============================
29 -minoff, r, h, 0,,, "Minimum required events in off bin for computing Noff and alpha"  
30 -emin, r, a, 0,,, "Lower energy limit (units of TeV; 0 => no cut)"  
31 -emax, r, a, 0,,, "Upper energy limit (units of TeV; 0 => no cut)"  
32 -sig_thr, r, h, 4.5,,, "Significance threshold for variability detection"  
33 -smoothkrnl, s, h, NONE, GAUSSIAN|DISK|NONE,, "Smoothing kernel for counts maps"  
34 -smoothpar, r, h, 0.05,,, "Parameter for the smoothing kernel" 30 +coordsys, s, a, CEL,CEL|GAL,,"Coordinate system (CEL - celestial, GAL - galactic)"
  31 +xsrc, r, a, 83.63,0,360, "First coordinate of the source of interest (RA or galactic l)"
  32 +ysrc, r, a, 22.01,-90,90, "Second coordinate of the source of interest (DEC or galactic b)"
  33 +emin, r, a, 0.1,,, "Lower energy limit (TeV)"
  34 +emax, r, a, 100.0,,, "Upper energy limit (TeV)"
  35 +threshold, r, h, 4.5,,, "Significance threshold for variability detection"
  36 +minoff, r, h, 0,,, "Minimum required events in off bin for computing Noff and alpha"
  37 +smooth_kernel, s, h, NONE, GAUSSIAN|DISK|NONE,, "Smoothing kernel for counts maps"
  38 +smooth_rad, r, h, 0.05,,, "Smoothing kernel radius (deg)"
35 39
36 # 40 #
37 # Time binning parameters 41 # Time binning parameters
@@ -47,12 +51,9 @@ usepnt, b, h, no,,, &quot;Use pointing instead of xref/yref parameters?&quot; @@ -47,12 +51,9 @@ usepnt, b, h, no,,, &quot;Use pointing instead of xref/yref parameters?&quot;
47 nxpix, i, a, 200,,, "Size of the X axis in pixels" 51 nxpix, i, a, 200,,, "Size of the X axis in pixels"
48 nypix, i, a, 200,,, "Size of the Y axis in pixels" 52 nypix, i, a, 200,,, "Size of the Y axis in pixels"
49 binsz, r, a, 0.02,,, "Image scale (in degrees/pixel)" 53 binsz, r, a, 0.02,,, "Image scale (in degrees/pixel)"
50 -coordsys, s, a, CEL,CEL|GAL,,"Coordinate system (CEL - celestial, GAL - galactic)"  
51 proj, s, a, CAR,AIT|AZP|CAR|GLS|MER|MOL|SFL|SIN|STG|TAN,, "Projection method" 54 proj, s, a, CAR,AIT|AZP|CAR|GLS|MER|MOL|SFL|SIN|STG|TAN,, "Projection method"
52 xref, r, a, 83.63,0,360, "First coordinate of image center in degrees (RA or galactic l)" 55 xref, r, a, 83.63,0,360, "First coordinate of image center in degrees (RA or galactic l)"
53 yref, r, a, 22.51,-90,90, "Second coordinate of image center in degrees (DEC or galactic b)" 56 yref, r, a, 22.51,-90,90, "Second coordinate of image center in degrees (DEC or galactic b)"
54 -xsrc, r, a, 83.63,0,360, "First coordinate of the source of interest (RA or galactic l)"  
55 -ysrc, r, a, 22.51,-90,90, "Second coordinate of the source of interest (DEC or galactic b)"  
56 57
57 # 58 #
58 # Standard parameters 59 # Standard parameters
test/test_ctfindvar.py
@@ -71,8 +71,9 @@ class Test(test): @@ -71,8 +71,9 @@ class Test(test):
71 71
72 # Setup ctfindvar command 72 # Setup ctfindvar command
73 cmd = ctfindvar+' inobs="'+self._events+'"'+\ 73 cmd = ctfindvar+' inobs="'+self._events+'"'+\
  74 + ' outmap="ctfindvar_cmd1.fits"'+\
  75 + ' outmodel="ctfindvar_cmd1.xml"'+\
74 ' logfile="ctfindvar_cmd1.log" chatter=1'+\ 76 ' logfile="ctfindvar_cmd1.log" chatter=1'+\
75 - ' prefix=ctfindvar_test_ '+\  
76 ' tmin=NONE tmax=NONE tinterval=20'+\ 77 ' tmin=NONE tmax=NONE tinterval=20'+\
77 ' emin=0.2 emax=1.1'+\ 78 ' emin=0.2 emax=1.1'+\
78 ' nxpix=200 nypix=200 binsz=0.05'+\ 79 ' nxpix=200 nypix=200 binsz=0.05'+\
@@ -80,12 +81,28 @@ class Test(test): @@ -80,12 +81,28 @@ class Test(test):
80 ' xref=83.63 yref=22.51'+\ 81 ' xref=83.63 yref=22.51'+\
81 ' xsrc=83.63 ysrc=22.51'+\ 82 ' xsrc=83.63 ysrc=22.51'+\
82 ' caldb=prod2 irf=South_0.5h' 83 ' caldb=prod2 irf=South_0.5h'
83 -  
84 - 84 +
85 # Check if execution was successful 85 # Check if execution was successful
86 self.test_value(self._execute(cmd), 0, 86 self.test_value(self._execute(cmd), 0,
87 'Check successful execution from command line') 87 'Check successful execution from command line')
88 88
  89 + # Setup ctfindvar command
  90 + cmd = ctfindvar+' inobs="event_file_that_does_not_exist.fits"'+ \
  91 + ' outmap="ctfindvar_cmd2.fits"'+ \
  92 + ' outmodel="ctfindvar_cmd2.xml"'+ \
  93 + ' logfile="ctfindvar_cmd2.log" chatter=1'+ \
  94 + ' tmin=NONE tmax=NONE tinterval=20'+ \
  95 + ' emin=0.2 emax=1.1'+ \
  96 + ' nxpix=200 nypix=200 binsz=0.05'+ \
  97 + ' proj=CAR coordsys=CEL'+ \
  98 + ' xref=83.63 yref=22.51'+ \
  99 + ' xsrc=83.63 ysrc=22.51'+ \
  100 + ' caldb=prod2 irf=South_0.5h'
  101 +
  102 + # Check if execution was successful
  103 + self.test_assert(self._execute(cmd, success=False) != 0,
  104 + 'Check invalid input file when executed from command line')
  105 +
89 # Check ctfindvar --help 106 # Check ctfindvar --help
90 self._check_help(ctfindvar) 107 self._check_help(ctfindvar)
91 108
@@ -101,11 +118,8 @@ class Test(test): @@ -101,11 +118,8 @@ class Test(test):
101 # Allocate ctfindvar 118 # Allocate ctfindvar
102 ctfindvar = ctools.ctfindvar() 119 ctfindvar = ctools.ctfindvar()
103 120
104 - #setup ctfindvar 121 + # Setup ctfindvar
105 ctfindvar['inobs'] = self._events 122 ctfindvar['inobs'] = self._events
106 - ctfindvar['logfile'] = 'ctfindvar_py0.log'  
107 - ctfindvar['chatter'] = 1  
108 - ctfindvar['prefix'] = 'ctfindvar_test_python_'  
109 ctfindvar['tmin'] = 'NONE' 123 ctfindvar['tmin'] = 'NONE'
110 ctfindvar['tmax'] = 'NONE' 124 ctfindvar['tmax'] = 'NONE'
111 ctfindvar['tinterval'] = 20 125 ctfindvar['tinterval'] = 20
@@ -122,32 +136,39 @@ class Test(test): @@ -122,32 +136,39 @@ class Test(test):
122 ctfindvar['ysrc'] = 22.51 136 ctfindvar['ysrc'] = 22.51
123 ctfindvar['caldb'] = 'prod2' 137 ctfindvar['caldb'] = 'prod2'
124 ctfindvar['irf'] = 'South_0.5h' 138 ctfindvar['irf'] = 'South_0.5h'
  139 + ctfindvar['outmap'] = 'ctfindvar_py0.fits'
  140 + ctfindvar['outmodel'] = 'ctfindvar_py0.xml'
  141 + ctfindvar['logfile'] = 'ctfindvar_py0.log'
  142 + ctfindvar['chatter'] = 1
125 143
126 - ## Run ctfindvar tool 144 + # Run ctfindvar tool
127 ctfindvar.logFileOpen() 145 ctfindvar.logFileOpen()
128 ctfindvar.run() 146 ctfindvar.run()
129 147
130 # Check that saving does not nothing 148 # Check that saving does not nothing
131 - ctfindvar['logfile'] = 'ctfindvar_py1.log' 149 + ctfindvar['outmap'] = 'ctfindvar_py1.fits'
  150 + ctfindvar['outmodel'] = 'ctfindvar_py1.xml'
  151 + ctfindvar['logfile'] = 'ctfindvar_py1.log'
132 ctfindvar.logFileOpen() 152 ctfindvar.logFileOpen()
133 ctfindvar.save() 153 ctfindvar.save()
134 154
135 # Check result 155 # Check result
136 - self._check_result_file('ctfindvar_test_python_signifmap.fits') 156 + self._check_result_file('ctfindvar_py1.fits')
137 157
138 # Copy ctfindvar tool 158 # Copy ctfindvar tool
139 cpy_tool = ctfindvar 159 cpy_tool = ctfindvar
140 160
141 # Run copy of ctfindvar tool again 161 # Run copy of ctfindvar tool again
142 - cpy_tool['logfile'] = 'ctfindvar_py2.log'  
143 - cpy_tool['chatter'] = 3  
144 - cpy_tool['prefix'] = 'ctfindvar_test_copytool_' 162 + cpy_tool['outmap'] = 'ctfindvar_py2.fits'
  163 + cpy_tool['outmodel'] = 'ctfindvar_py2.xml'
  164 + cpy_tool['logfile'] = 'ctfindvar_py2.log'
  165 + cpy_tool['chatter'] = 3
145 cpy_tool.logFileOpen() 166 cpy_tool.logFileOpen()
146 cpy_tool.run() 167 cpy_tool.run()
147 cpy_tool.save() 168 cpy_tool.save()
148 169
149 # Check result 170 # Check result
150 - self._check_result_file('ctfindvar_test_copytool_signifmap.fits') 171 + self._check_result_file('ctfindvar_py2.fits')
151 172
152 # Check that clearing does not lead to an exception or segfault 173 # Check that clearing does not lead to an exception or segfault
153 ctfindvar.clear() 174 ctfindvar.clear()
@@ -158,17 +179,16 @@ class Test(test): @@ -158,17 +179,16 @@ class Test(test):
158 # Check ctfindvar result 179 # Check ctfindvar result
159 def _check_result_file(self, filename): 180 def _check_result_file(self, filename):
160 """ 181 """
161 - Check content of tool 182 + Check ctfindvar result
162 183
163 Parameters 184 Parameters
164 ---------- 185 ----------
165 - tool : `~ctools.ctfindvar`  
166 - ctfindvar instance 186 + filename : str
  187 + Output file
167 """ 188 """
168 - print "checking if you amazing file is valid"  
169 - # Read variability file  
170 - fits = gammalib.GFits(filename)  
171 - self.test_value(fits.size(), 3,  
172 - 'Check for 3 rows in srcsig file') 189 + # Read variability file
  190 + fits = gammalib.GFits(filename)
  191 + self.test_value(fits.size(), 3, 'Check for 3 extensions in output file')
  192 +
173 # Return 193 # Return
174 return 194 return