Commit 89ab90bbe67d8ce12d5553175505e87d4bdc74e7

Authored by Jürgen Knödlseder
2 parents 5f3ab77e ec7d56af

Merge branch '00-07-02' into integration

Conflicts:
	ChangeLog
	scripts/cta_root2caldb.py
	src/ctlike/ctlike.par
	src/ctobssim/ctobssim.par
ChangeLog
@@ -6,12 +6,13 @@ @@ -6,12 +6,13 @@
6 Support energy dispersion in ctobssim, ctlike and ctmodel 6 Support energy dispersion in ctobssim, ctlike and ctmodel
7 7
8 8
9 -2014-02-10 Juergen Knoedlseder <jurgen.knodlseder@irap.omp.eu> 9 +2014-02-20 Juergen Knoedlseder <jurgen.knodlseder@irap.omp.eu>
10 10
11 * Bug fix Version 0.7.2 released 11 * Bug fix Version 0.7.2 released
12 ================================ 12 ================================
13 13
14 Add logL and event statistics to cspull output file 14 Add logL and event statistics to cspull output file
  15 + Properly close temporary file in ctselect (#1149)
15 16
16 17
17 2014-02-05 Juergen Knoedlseder <jurgen.knodlseder@irap.omp.eu> 18 2014-02-05 Juergen Knoedlseder <jurgen.knodlseder@irap.omp.eu>
@@ -2,7 +2,7 @@ New Features and Important Changes in ctools 00-08-00 @@ -2,7 +2,7 @@ New Features and Important Changes in ctools 00-08-00
2 2
3 Author(s) : Juergen Knoedlseder 3 Author(s) : Juergen Knoedlseder
4 4
5 -10 February 2014 5 +20 February 2014
6 6
7 7
8 1. Introduction 8 1. Introduction
@@ -31,7 +31,8 @@ dispersion can be switched on or off using the hidden edisp parameter. @@ -31,7 +31,8 @@ dispersion can be switched on or off using the hidden edisp parameter.
31 31
32 5. ctselect - CTA event selection 32 5. ctselect - CTA event selection
33 --------------------------------- 33 ---------------------------------
34 -None 34 +The temporary file is now properly close to avoid file opening errors
  35 +when iterating over ctselect (#1149).
35 36
36 37
37 6. ctbin - CTA event binning 38 6. ctbin - CTA event binning
caldb/Makefile.am
1 ## Process this file with automake to produce Makefile.in 1 ## Process this file with automake to produce Makefile.in
2 2
3 # Calibration files to distribute 3 # Calibration files to distribute
4 -irfs = cta_dummy_irf.dat 4 +caldb_indx = caldb.indx
  5 +caldb_irf = cta_dummy_irf.dat
5 6
6 -caldbdir = $(datadir)/caldb/data/cta/dummy  
7 -caldb_DATA = $(irfs)  
8 -  
9 -# Extra files to be distributed in the tar file  
10 -EXTRA_DIST = $(irfs) 7 +# Distribute calibration database files
  8 +EXTRA_DIST = $(caldb_indx) $(caldb_irf)
  9 +caldbindxdir = $(datadir)/caldb/data/cta/dummy
  10 +caldbindx_DATA = $(caldb_indx)
  11 +caldbirfdir = $(datadir)/caldb/data/cta/dummy/bcf
  12 +caldbirf_DATA = $(caldb_irf)
caldb/caldb.indx 0 → 100644
No preview for this file type
doc/source/_templates/index.html
@@ -34,7 +34,8 @@ CTA format), Fermi/LAT and COMPTEL data. {%endtrans%}&lt;/p&gt; @@ -34,7 +34,8 @@ CTA format), Fermi/LAT and COMPTEL data. {%endtrans%}&lt;/p&gt;
34 34
35 <ul> 35 <ul>
36 <li><a class="smalllink" href="{{ pathto("user_manual/getting") }}">{%trans%}Installation{%endtrans%}</a></li> 36 <li><a class="smalllink" href="{{ pathto("user_manual/getting") }}">{%trans%}Installation{%endtrans%}</a></li>
37 -<li><a class="smalllink" href="{{ pathto("user_manual/quickstart") }}">{%trans%}Quickstart{%endtrans%}</a></li> 37 +<li><a class="smalllink" href="{{ pathto("user_manual/getting_started/quickstart") }}">{%trans%}Quickstart{%endtrans%}</a></li>
  38 +<li><a class="smalllink" href="{{ pathto("user_manual/getting_started/response") }}">{%trans%}CTA response functions{%endtrans%}</a></li>
38 <li><a class="smalllink" href="{{ pathto("user_manual/index") }}">{%trans%}User Manual{%endtrans%}</a></li> 39 <li><a class="smalllink" href="{{ pathto("user_manual/index") }}">{%trans%}User Manual{%endtrans%}</a></li>
39 <li><a class="smalllink" href="{{ pathto("documentation") }}">{%trans%}ctools reference{%endtrans%}</a></li> 40 <li><a class="smalllink" href="{{ pathto("documentation") }}">{%trans%}ctools reference{%endtrans%}</a></li>
40 <li><a class="smalllink" href="doxygen/index.html">{%trans%}Source code documentation (Doxygen){%endtrans%}</a></li> 41 <li><a class="smalllink" href="doxygen/index.html">{%trans%}Source code documentation (Doxygen){%endtrans%}</a></li>
doc/source/conf.py
@@ -51,7 +51,7 @@ copyright = u&#39;2014, ctools developers&#39; @@ -51,7 +51,7 @@ copyright = u&#39;2014, ctools developers&#39;
51 version = '0.7' 51 version = '0.7'
52 52
53 # The full version, including alpha/beta/rc tags. 53 # The full version, including alpha/beta/rc tags.
54 -release = '0.7.1' 54 +release = '0.7.2'
55 55
56 # The language for content autogenerated by Sphinx. Refer to documentation 56 # The language for content autogenerated by Sphinx. Refer to documentation
57 # for a list of supported languages. 57 # for a list of supported languages.
@@ -165,7 +165,7 @@ html_show_copyright = False @@ -165,7 +165,7 @@ html_show_copyright = False
165 #html_file_suffix = None 165 #html_file_suffix = None
166 166
167 # Output file base name for HTML help builder. 167 # Output file base name for HTML help builder.
168 -htmlhelp_basename = 'GammaLibdoc' 168 +htmlhelp_basename = 'ctoolsdoc'
169 169
170 170
171 # -- Options for LaTeX output -------------------------------------------------- 171 # -- Options for LaTeX output --------------------------------------------------
doc/source/user_manual/cntmap-fits.jpg renamed to doc/source/user_manual/getting_started/cntmap-fits.jpg

47 KB

doc/source/user_manual/cntmap-map.jpg renamed to doc/source/user_manual/getting_started/cntmap-map.jpg

22.7 KB

doc/source/user_manual/dskeys.jpg renamed to doc/source/user_manual/getting_started/dskeys.jpg

99.6 KB

doc/source/user_manual/getting_started/faq.rst 0 → 100644
  1 +Frequently asked questions
  2 +--------------------------
  3 +
  4 +To be written ...
doc/source/user_manual/getting_started/index.rst 0 → 100644
  1 +Getting started
  2 +===============
  3 +
  4 +.. toctree::
  5 + :maxdepth: 2
  6 +
  7 + quickstart
  8 + response
  9 + models
  10 + python
  11 + faq
  12 +
doc/source/user_manual/getting_started/irfs.jpg 0 → 100644

27.6 KB

doc/source/user_manual/getting_started/models.rst 0 → 100644
  1 +Models available in ctools
  2 +--------------------------
  3 +
  4 +To be written ... (you may read
  5 +http://gammalib.sourceforge.net/user_manual/modules/model.html#spectral-models for the time being).
doc/source/user_manual/getting_started/python.rst 0 → 100644
  1 +Using ctools from Python
  2 +------------------------
  3 +
  4 +To be written ...
doc/source/user_manual/quickstart.rst renamed to doc/source/user_manual/getting_started/quickstart.rst
1 -Getting started  
2 -===============  
3 -  
4 A quick ctools tutorial 1 A quick ctools tutorial
5 ----------------------- 2 -----------------------
6 3
@@ -641,22 +638,3 @@ the same events for both analyses. @@ -641,22 +638,3 @@ the same events for both analyses.
641 2014-01-09T21:47:54: +==============+ 638 2014-01-09T21:47:54: +==============+
642 2014-01-09T21:47:54: 639 2014-01-09T21:47:54:
643 2014-01-09T21:47:54: Application "ctlike" terminated after 6 wall clock seconds, consuming 0.163384 seconds of CPU time. 640 2014-01-09T21:47:54: Application "ctlike" terminated after 6 wall clock seconds, consuming 0.163384 seconds of CPU time.
644 -  
645 -  
646 -Models available in ctools  
647 ---------------------------  
648 -  
649 -To be written ... (you may read  
650 -http://gammalib.sourceforge.net/user_manual/modules/model.html#spectral-models for the time being).  
651 -  
652 -  
653 -Using ctools from Python  
654 -------------------------  
655 -  
656 -To be written ...  
657 -  
658 -  
659 -Frequently asked questions  
660 ---------------------------  
661 -  
662 -To be written ...  
doc/source/user_manual/getting_started/response.rst 0 → 100644
  1 +CTA Instrument Response Functions
  2 +---------------------------------
  3 +
  4 +.. note ::
  5 +
  6 + Instrument response functions are formally not part of ctools as they
  7 + should be provided by the instrument teams. The GammaLib framework on
  8 + which ctools are built comes with a set of basic response functions, but
  9 + for getting the latest instrument response functions you should contact
  10 + the relevant instrument teams. CTA consortium members should
  11 + `download
  12 + <https://portal.cta-observatory.org/WG/DM/DM_wiki/DATA_Access/Pages/Science%20Tools.aspx>`_
  13 + the latest calibration database containing Prod1 and Prod2 instrument
  14 + response functions from the consortium Sharepoint site (requires
  15 + password).
  16 +
  17 +
  18 +What are instrument response functions?
  19 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  20 +
  21 +The instrument response functions provide a mathematical description that
  22 +links the measured quantities :math:`\vec{d}` of an event to the physical
  23 +quantities :math:`\vec{p}` of the incident photon. The following figure
  24 +illustrates this relationship:
  25 +
  26 +.. figure:: irfs.jpg
  27 + :width: 70%
  28 +
  29 +:math:`I(\vec{p})` is the gamma-ray intensity arriving at Earth as a
  30 +function of photon properties :math:`\vec{p}`
  31 +(which usually are true photon energy, true photon incident direction,
  32 +and true photon arrival time),
  33 +while :math:`e(\vec{d})` is the expected event rate as function of event
  34 +properties :math:`\vec{d}` (which usually are the measured photon energy,
  35 +measured or reconstructued photon incident direction, and measured photon
  36 +arrival time). The expected event rate is obtained by integrating the
  37 +product of
  38 +the instrumental response function :math:`R(\vec{d}|\vec{p},\vec{a}`)
  39 +and the emitted intensity :math:`I(\vec{p})` over the photon properties
  40 +:math:`\vec{p}`.
  41 +The argument :math:`\vec{a}` in the response function comprises any
  42 +auxiliary parameter on which the response function may depend on (e.g.
  43 +pointing direction, triggered telescopes, optical efficiencies,
  44 +atmospheric conditions, etc.). All these quantities and hence the
  45 +instrument response function may depend on time.
  46 +
  47 +
  48 +Data formats
  49 +~~~~~~~~~~~~
  50 +
  51 +So far the data format of the CTA instrument response functions has not
  52 +yet been settled, and information about instrument response functions is
  53 +provided in various ways. Three types of data formats are supported:
  54 +
  55 +- :ref:`sec_cta_rsp_perftable`
  56 +
  57 +- :ref:`sec_cta_rsp_xspec`
  58 +
  59 +- :ref:`sec_cta_rsp_rsptable`
  60 +
  61 +All formats provide the instrument response as function of true (and
  62 +sometimes also measured) photon energy, typically from about 20 GeV to
  63 +about 125 TeV.
  64 +Performance tables provide the instrument response for on-axis sources.
  65 +ARF, RMF and PSF files provide the instrument response for a specific
  66 +source position (and region) within the field of view.
  67 +Response tables provide the instrument response as function of position in
  68 +the field of view. Response tables are thus the most universal form of
  69 +instrument response functions available, and we recommend using this form
  70 +for any analysis.
  71 +
  72 +.. note ::
  73 +
  74 + Instrument response functions are so far only provided for a fixed
  75 + zenith angle of 20 deg. Therefore, no zenith or azimuth angle dependence has
  76 + been implemented so far.
  77 +
  78 +
  79 +Installing the CTA calibration database
  80 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  81 +
  82 +After `downloading
  83 +<https://portal.cta-observatory.org/WG/DM/DM_wiki/DATA_Access/Pages/Science%20Tools.aspx>`_
  84 +the latest calibration database (only possible for CTA consortium members),
  85 +the database is installed using
  86 +
  87 +.. code-block:: bash
  88 +
  89 + $ [sudo] tar -C $CALDB -zxvf cta-caldb-20140220.tar.gz
  90 +
  91 +or
  92 +
  93 +.. code-block:: bash
  94 +
  95 + $ [sudo] tar -C $CALDB -xvf cta-caldb-20140220.tar
  96 +
  97 +depending on whether the database has been retrieved as a gzipped file or
  98 +not. Depending on your access rights to the ``$CALDB`` directory,
  99 +installation of the calibration database may require root privileges
  100 +(type ``sudo`` in this case, otherwise omit this part of the command).
  101 +
  102 +The calibration database must be installed into the directory to which you
  103 +``CALDB`` environment variable points. By default, ctools sets this
  104 +enviroment variable to
  105 +
  106 +.. code-block:: bash
  107 +
  108 + $CTOOLS/share/caldb
  109 +
  110 +but it may be that due to some other analysis software on your system the
  111 +``CALDB`` environment variable points to another directory. If in doubt, type
  112 +
  113 +.. code-block:: bash
  114 +
  115 + echo $CALDB
  116 +
  117 +to find out to which directory your ``CALDB`` environment variable points.
  118 +You may always overwrite this setting using
  119 +
  120 +.. code-block:: bash
  121 +
  122 + export CALDB=/my/preferred/caldb/directory
  123 +
  124 +which you can add to your ``.bashrc`` file to set the directory
  125 +permanently.
  126 +ctools will use the ``CALDB`` environment variable to find out where the
  127 +calibration database is located. If needed, this mechanism can be
  128 +circumvented (see :ref:`sec_cta_rsp_abspath`).
  129 +
  130 +
  131 +Specifying the CTA Instrument Response Functions
  132 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  133 +
  134 +The specification of the CTA Instrument Response Functions depends on the
  135 +way how ctools are used. Common to all methods is that the IRFs are
  136 +defined by a response name and a calibration database name.
  137 +
  138 +There are different means to specify the CTA Instrument Response Functions
  139 +when using ctools, and the following section describe the
  140 +
  141 +
  142 +Specifying the response function as input parameters
  143 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  144 +
  145 +ctools that require instrument response functions have two parameters
  146 +to specify the calibration database name and the response function name.
  147 +The following example shows a ``ctobssim`` run using the ``dummy``
  148 +calibration database and the ``cta_dummy_irf`` response function:
  149 +
  150 +.. code-block:: bash
  151 +
  152 + $ ctobssim
  153 + Model [$CTOOLS/share/models/crab.xml]
  154 + Calibration database [dummy]
  155 + Instrument response function [cta_dummy_irf]
  156 + RA of pointing (degrees) (0-360) [83.63]
  157 + Dec of pointing (degrees) (-90-90) [22.01]
  158 + Radius of FOV (degrees) (0-180) [5.0]
  159 + Start time (MET in s) (0) [0.0]
  160 + End time (MET in s) (0) [1800.0]
  161 + Lower energy limit (TeV) (0) [0.1]
  162 + Upper energy limit (TeV) (0) [100.0]
  163 + Output event data file or observation definition file [events.fits]
  164 +
  165 +Running the other tools is equivalent.
  166 +
  167 +
  168 +Specifying the response function in an observation definition file
  169 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  170 +
  171 +In the above example, only a single global response function can be
  172 +used for all CTA observations. If you need to specify response functions
  173 +per observation you can add the information directly in the XML observation
  174 +definition file:
  175 +
  176 +.. code-block:: xml
  177 +
  178 + <observation_list title="observation library">
  179 + <observation name="Crab" id="00001" instrument="CTA">
  180 + <parameter name="EventList" file="events.fits"/>
  181 + <parameter name="Calibration" database="dummy" response="cta_dummy_irf"/>
  182 + </observation>
  183 + </observation_list>
  184 +
  185 +The ``Calibration`` parameter specifies the calibration database and
  186 +response name. You can then pass this file directly to, e.g., ``ctlike``:
  187 +
  188 +.. code-block:: bash
  189 +
  190 + $ ctlike
  191 + Event list, counts map or observation definition file [events.fits] obs_rsp.xml
  192 + Source model [$CTOOLS/share/models/crab.xml]
  193 + Source model output file [crab_results.xml]
  194 +
  195 +Note that ``ctlike`` does not ask for the calibration database and
  196 +response name as it found the relevant information in the XML file.
  197 +
  198 +.. _sec_cta_rsp_abspath:
  199 +
  200 +
  201 +Specifying individual instrument response files
  202 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  203 +
  204 +If you need even more control over individual response files, you can
  205 +specify them individually in the XML observation file as follows:
  206 +
  207 +.. code-block:: xml
  208 +
  209 + <observation_list title="observation library">
  210 + <observation name="Crab" id="00001" instrument="CTA">
  211 + <parameter name="EventList" file="events.fits"/>
  212 + <parameter name="EffectiveArea" file="$CALDB/data/cta/dummy/bcf/cta_dummy_irf.dat"/>
  213 + <parameter name="PointSpreadFunction" file="$CALDB/data/cta/dummy/bcf/cta_dummy_irf.dat"/>
  214 + <parameter name="EnergyDispersion" file="$CALDB/data/cta/dummy/bcf/cta_dummy_irf.dat"/>
  215 + <parameter name="Background" file="$CALDB/data/cta/dummy/bcf/cta_dummy_irf.dat"/>
  216 + </observation>
  217 + </observation_list>
  218 +
  219 +
  220 +From within a Python script
  221 +^^^^^^^^^^^^^^^^^^^^^^^^^^^
  222 +
  223 +The following example illustrates how to set the calibration database
  224 +and response name from within Python:
  225 +
  226 +.. code-block:: python
  227 +
  228 + import gammalib
  229 + obs = gammalib.GCTAObservation()
  230 + caldb = gammalib.GCaldb("cta", "dummy")
  231 + irf = "cta_dummy_irf"
  232 + obs.response(irf, caldb)
  233 +
  234 +The calibration database is set by creating a ``GCaldb`` object. The
  235 +constructor takes as argument the mission (always ``cta``) and the
  236 +database name, in our case ``dummy``. The response function is then set
  237 +by passing the response name (here ``cta_dummy_irf``) and the calibration
  238 +database object to the ``response`` method.
  239 +
  240 +Alternatively, you can specify a specific response file by using
  241 +
  242 +.. code-block:: python
  243 +
  244 + import gammalib
  245 + obs = gammalib.GCTAObservation()
  246 + caldb = gammalib.GCaldb("$CALDB/data/cta/dummy/bcf")
  247 + irf = "cta_dummy_irf.dat"
  248 + obs.response(irf, caldb)
  249 +
  250 +Here, the directory in which the calibration file resides is specified
  251 +as argument to the ``GCaldb`` constructor, and the filename is passed
  252 +as response function name to the ``response`` method.
  253 +
  254 +
  255 +What response functions are available?
  256 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  257 +
  258 +To see what response functions are available on your system, type
  259 +
  260 +.. code-block:: bash
  261 +
  262 + $ cscaldb
  263 +
  264 +This creates the file ``cscaldb.log`` in your current working directory
  265 +that contains information about the available response functions. The
  266 +format of the file is:
  267 +
  268 +.. code-block:: bash
  269 +
  270 + 2014-02-20T17:15:21: +==============+
  271 + 2014-02-20T17:15:21: | Mission: cta |
  272 + 2014-02-20T17:15:21: +==============+
  273 + 2014-02-20T17:15:21: === Calibration: aar ===
  274 + 2014-02-20T17:15:21: DESY20140105_50h
  275 + 2014-02-20T17:15:21: DESY20140105_50h_0deg
  276 + 2014-02-20T17:15:21: DESY20140105_50h_180deg
  277 + 2014-02-20T17:15:21:
  278 + 2014-02-20T17:15:21: === Calibration: aar500 ===
  279 + 2014-02-20T17:15:21: DESY20140105_50h
  280 + 2014-02-20T17:15:21: DESY20140105_50h_0deg
  281 + 2014-02-20T17:15:21: DESY20140105_50h_180deg
  282 +
  283 +The name after ``Calibration`` is what we called earlier the calibration
  284 +database, and the names that are following each
  285 +``=== Calibration: xxx ===`` header are the response file names.
  286 +
  287 +To get the same information also onto the console, type
  288 +
  289 +.. code-block:: bash
  290 +
  291 + $ cscaldb debug=yes
  292 +
  293 +
  294 +Data format details
  295 +~~~~~~~~~~~~~~~~~~~
  296 +
  297 +.. _sec_cta_rsp_perftable:
  298 +
  299 +Performance tables
  300 +^^^^^^^^^^^^^^^^^^
  301 +
  302 +In the early days, the instrument performances derived from Monte-Carlo
  303 +simulations have been summarised in what we call here Performance
  304 +Tables, which are ASCII files that contain as function of energy the
  305 +on-axis performance parameters of CTA, such as effective area, point spread
  306 +function containment radius, energy resolution, background count rate and
  307 +differential sensitivity.
  308 +
  309 +Below an example of a CTA performance table::
  310 +
  311 + log(E) Area r68 r80 ERes. BG Rate Diff Sens
  312 + -1.7 261.6 0.3621 0.4908 0.5134 0.0189924 6.88237e-11
  313 + -1.5 5458.2 0.2712 0.3685 0.4129 0.1009715 1.72717e-11
  314 + -1.3 15590.0 0.1662 0.2103 0.2721 0.0575623 6.16963e-12
  315 + -1.1 26554.1 0.1253 0.1567 0.2611 0.0213008 2.89932e-12
  316 + -0.9 52100.5 0.1048 0.1305 0.1987 0.0088729 1.39764e-12
  317 + -0.7 66132.1 0.0827 0.1024 0.1698 0.0010976 6.03531e-13
  318 + -0.5 108656.8 0.0703 0.0867 0.1506 0.0004843 3.98147e-13
  319 + -0.3 129833.0 0.0585 0.0722 0.1338 0.0001575 3.23090e-13
  320 + -0.1 284604.3 0.0531 0.0656 0.1008 0.0001367 2.20178e-13
  321 + 0.1 263175.3 0.0410 0.0506 0.0831 0.0000210 1.87452e-13
  322 + 0.3 778048.6 0.0470 0.0591 0.0842 0.0000692 1.53976e-13
  323 + 0.5 929818.8 0.0391 0.0492 0.0650 0.0000146 1.18947e-13
  324 + 0.7 1078450.0 0.0335 0.0415 0.0541 0.0000116 1.51927e-13
  325 + 0.9 1448579.1 0.0317 0.0397 0.0516 0.0000047 1.42439e-13
  326 + 1.1 1899905.0 0.0290 0.0372 0.0501 0.0000081 1.96670e-13
  327 + 1.3 2476403.8 0.0285 0.0367 0.0538 0.0000059 2.20695e-13
  328 + 1.5 2832570.6 0.0284 0.0372 0.0636 0.0000073 3.22523e-13
  329 + 1.7 3534065.3 0.0290 0.0386 0.0731 0.0000135 4.84153e-13
  330 + 1.9 3250103.4 0.0238 0.0308 0.0729 0.0000044 6.26265e-13
  331 + 2.1 3916071.6 0.0260 0.0354 0.0908 0.0000023 7.69921e-13
  332 + ---------------------------------------------
  333 + 1) log(E) = log10(E/TeV) - bin centre
  334 + 2) Eff Area - in square metres after background cut (no theta cut)
  335 + 3) Ang. Res - 68% containment radius of gamma-ray PSF post cuts - in degrees
  336 + 4) Ang. Res - 80% containment radius of gamma-ray PSF post cuts - in degrees
  337 + 5) Fractional Energy Resolution (rms)
  338 + 6) BG Rate - inside point-source selection region - post call cuts - in Hz
  339 + 7) Diff Sens - differential sensitivity for this bin expressed as E^2 dN/dE
  340 + - in erg cm^-2 s^-1 - for a 50 hours exposure - 5 sigma significance including
  341 + systematics and statistics and at least 10 photons.
  342 +
  343 +
  344 +.. _sec_cta_rsp_xspec:
  345 +
  346 +ARF, RMF and PSF files
  347 +^^^^^^^^^^^^^^^^^^^^^^
  348 +
  349 +Instrument response information for the first CTA Data Challenge (1DC) has been
  350 +provided in a format that was heavily inspired by the
  351 +`RMF and ARF file formats
  352 +<http://heasarc.gsfc.nasa.gov/docs/heasarc/caldb/docs/memos/cal_gen_92_002/cal_gen_92_002.html>`_
  353 +that have been introduced by
  354 +`HEASARC <http://heasarc.gsfc.nasa.gov/>`_
  355 +for spectral analysis of X-ray data.
  356 +
  357 +The Redistribution Matrix File (RMF) describes how an incoming photon with
  358 +a given true energy is redistributed in measured energy. In other words, it
  359 +describes the energy dispersion of the instrument. The RMF is organised as
  360 +a two-dimensional matrix, with the first axis being in true energies while
  361 +the second axis represents the measured energies.
  362 +
  363 +The Ancillary Response File (ARF) describes the sensitivity of the
  364 +instrument to photons of a given true energy. The ARF gives the effective
  365 +area of the detector system after applying an event selection cut
  366 +(theta cut). For computing the ARF, some knowledge of the Point Spread
  367 +Function (PSF) is needed, so that the fraction of photons that falls into
  368 +the event selection region for a given source can be properly estimated.
  369 +Once this is done, no further PSF information is needed for the analysis.
  370 +
  371 +ctools however relies on the full modelling of the instrument, including
  372 +the Point Spread Function, and hence, PSF information has also been
  373 +provided for 1DC. Two data formats have been used for this: a first that
  374 +is based on a simple one-dimensional vector, providing the width of a
  375 +2-dimensional Gaussian as function of energy; and a second that is based
  376 +on a three-component 2D Gaussian as function of energy and offset angle
  377 +in the camera system. While the former was implement by a simply FITS
  378 +table with two columns, the latter was implemented by
  379 +:ref:`sec_cta_rsp_rsptable`.
  380 +
  381 +
  382 +.. _sec_cta_rsp_rsptable:
  383 +
  384 +Response tables
  385 +^^^^^^^^^^^^^^^
  386 +
  387 +The CTA response table class ``GCTAResponseTable`` provides a generic
  388 +handle for multi-dimensional response information. It is based on the
  389 +response format used for storing response information for the
  390 +*Fermi*/LAT telescope. In this format, all information is stored in
  391 +a single row of a FITS binary table. Each element of the row contains
  392 +a vector column, that describes the axes of the multi-dimensional response
  393 +cube and the response information. Note that this class may in the future
  394 +be promoted to the GammaLib core, as a similar class has been implemented
  395 +in the *Fermi*/LAT interface.
doc/source/user_manual/index.rst
@@ -7,7 +7,7 @@ User Manual @@ -7,7 +7,7 @@ User Manual
7 7
8 introduction 8 introduction
9 getting 9 getting
10 - quickstart 10 + getting_started/index
11 enhancing 11 enhancing
12 reference 12 reference
13 expert 13 expert
scripts/Makefile.am
@@ -4,12 +4,12 @@ @@ -4,12 +4,12 @@
4 binscripts = cta_kb_bkgrates.py cta_root2irf.py cta_make_nodes.py 4 binscripts = cta_kb_bkgrates.py cta_root2irf.py cta_make_nodes.py
5 5
6 # Scripts to be distributed in pythondir 6 # Scripts to be distributed in pythondir
7 -pythonscripts = cspull.py cssens.py cstsdist.py \ 7 +pythonscripts = cspull.py cssens.py cstsdist.py cscaldb.py \
8 obsutils.py \ 8 obsutils.py \
9 lsmodel.py 9 lsmodel.py
10 10
11 # Scripts for which symbolic links will be created in bindir 11 # Scripts for which symbolic links will be created in bindir
12 -linkscripts = cspull cssens cstsdist \ 12 +linkscripts = cspull cssens cstsdist cscaldb \
13 lsmodel 13 lsmodel
14 14
15 # Distribute scripts 15 # Distribute scripts
scripts/cscaldb.py 0 → 100755
  1 +#! /usr/bin/env python
  2 +# ==========================================================================
  3 +# This script dumps all available calibrations into the console.
  4 +#
  5 +# Copyright (C) 2014 Juergen Knoedlseder
  6 +#
  7 +# This program is free software: you can redistribute it and/or modify
  8 +# it under the terms of the GNU General Public License as published by
  9 +# the Free Software Foundation, either version 3 of the License, or
  10 +# (at your option) any later version.
  11 +#
  12 +# This program is distributed in the hope that it will be useful,
  13 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15 +# GNU General Public License for more details.
  16 +#
  17 +# You should have received a copy of the GNU General Public License
  18 +# along with this program. If not, see <http://www.gnu.org/licenses/>.
  19 +#
  20 +# ==========================================================================
  21 +import gammalib
  22 +import sys
  23 +import glob
  24 +import os
  25 +
  26 +
  27 +# ============ #
  28 +# cspull class #
  29 +# ============ #
  30 +class cscaldb(gammalib.GApplication):
  31 + """
  32 + This class implements the pull distribution generation script. It derives
  33 + from the GammaLib::GApplication class which provides support for parameter
  34 + files, command line arguments, and logging. In that way the Python
  35 + script behaves just as a regular ctool.
  36 + """
  37 + def __init__(self, *argv):
  38 + """
  39 + Constructor.
  40 + """
  41 + # Set name
  42 + self.name = "cscaldb"
  43 + self.version = "0.1.0"
  44 +
  45 + # Make sure that parfile exists
  46 + file = self.parfile()
  47 +
  48 + # Initialise application
  49 + if len(argv) == 0:
  50 + gammalib.GApplication.__init__(self, self.name, self.version)
  51 + elif len(argv) ==1:
  52 + gammalib.GApplication.__init__(self, self.name, self.version, *argv)
  53 + else:
  54 + raise TypeError("Invalid number of arguments given.")
  55 +
  56 + # Set logger properties
  57 + self.log_header()
  58 + self.log.date(True)
  59 +
  60 + # Return
  61 + return
  62 +
  63 + def __del__(self):
  64 + """
  65 + Destructor.
  66 + """
  67 + # Return
  68 + return
  69 +
  70 + def parfile(self):
  71 + """
  72 + Check if parfile exists. If parfile does not exist then create a
  73 + default parfile. This kluge avoids shipping the cscript with a parfile.
  74 + """
  75 + # Set parfile name
  76 + parfile = self.name+".par"
  77 +
  78 + try:
  79 + pars = gammalib.GApplicationPars(parfile)
  80 + except:
  81 + # Signal if parfile was not found
  82 + sys.stdout.write("Parfile "+parfile+" not found. Create default parfile.\n")
  83 +
  84 + # Create default parfile
  85 + pars = gammalib.GApplicationPars()
  86 + pars.append_standard()
  87 + pars.save(parfile)
  88 +
  89 + # Return
  90 + return
  91 +
  92 + def get_parameters(self):
  93 + """
  94 + Get parameters from parfile.
  95 + """
  96 + # Return
  97 + return
  98 +
  99 + def execute(self):
  100 + """
  101 + Execute the script.
  102 + """
  103 + # Run the script
  104 + self.run()
  105 +
  106 + # Return
  107 + return
  108 +
  109 + def run(self):
  110 + """
  111 + Run the script.
  112 + """
  113 + # Switch screen logging on in debug mode
  114 + if self.logDebug():
  115 + self.log.cout(True)
  116 +
  117 + # Get parameters
  118 + self.get_parameters()
  119 +
  120 + # Write input parameters into logger
  121 + if self.logTerse():
  122 + self.log_parameters()
  123 + self.log("\n")
  124 +
  125 + # Get the calibration database path
  126 + caldb = gammalib.GCaldb()
  127 +
  128 + # Gather missions
  129 + missions = []
  130 + paths = glob.glob(caldb.rootdir()+"/data/*")
  131 + paths.sort()
  132 + for path in paths:
  133 + head, tail = os.path.split(path)
  134 + missions.append(tail)
  135 +
  136 + # Loop over missions
  137 + for mission in missions:
  138 +
  139 + # Skip all non-CTA instruments
  140 + if mission != "cta":
  141 + continue
  142 +
  143 + # Write mission into logger
  144 + if self.logTerse():
  145 + self.log("\n")
  146 + self.log.header1("Mission: "+mission)
  147 +
  148 + # Gather instruments
  149 + instruments = []
  150 + paths = glob.glob(caldb.rootdir()+"/data/"+mission+"/*")
  151 + paths.sort()
  152 + for path in paths:
  153 + head, tail = os.path.split(path)
  154 + instruments.append(tail)
  155 +
  156 + # Loop over instruments
  157 + for instrument in instruments:
  158 +
  159 + # Write mission into logger
  160 + if self.logTerse():
  161 + self.log.header3("Calibration: "+instrument)
  162 +
  163 + # Open calibration index file
  164 + cifname = caldb.rootdir()+"/data/"+mission+"/"+instrument+"/caldb.indx"
  165 + fits = gammalib.GFits(cifname)
  166 + cif = fits["CIF"]
  167 + cals = cif["CAL_CBD"]
  168 +
  169 + # Extract response names
  170 + names = []
  171 + nrows = cals.length()
  172 + ncols = cals.number()
  173 + for row in range(nrows):
  174 + for col in range(ncols):
  175 + cal = cals.string(row, col)
  176 + istart = cal.find("NAME(")
  177 + if istart != -1:
  178 + istop = cal.find(")")
  179 + name = cal[5:istop]
  180 + if names.count(name) == 0:
  181 + names.append(name)
  182 + names.sort()
  183 +
  184 + # Print response name
  185 + if self.logTerse():
  186 + for name in names:
  187 + self.log(name+"\n")
  188 + self.log("\n")
  189 +
  190 + # Return
  191 + return
  192 +
  193 +
  194 +# ======================== #
  195 +# Main routine entry point #
  196 +# ======================== #
  197 +if __name__ == '__main__':
  198 + """
  199 + Dumps calibration database into log file.
  200 + """
  201 + # Create instance of application
  202 + app = cscaldb(sys.argv)
  203 +
  204 + # Open logfile
  205 + app.logFileOpen()
  206 +
  207 + # Execute application
  208 + app.execute()
  209 +
0 \ No newline at end of file 210 \ No newline at end of file
scripts/cssens.py
@@ -57,7 +57,7 @@ class cssens(GApplication): @@ -57,7 +57,7 @@ class cssens(GApplication):
57 """ 57 """
58 # Set name 58 # Set name
59 self.name = "cssens" 59 self.name = "cssens"
60 - self.version = "0.3.0" 60 + self.version = "0.4.0"
61 61
62 # Make sure that parfile exists 62 # Make sure that parfile exists
63 file = self.parfile() 63 file = self.parfile()
@@ -106,11 +106,11 @@ class cssens(GApplication): @@ -106,11 +106,11 @@ class cssens(GApplication):
106 # Create default parfile 106 # Create default parfile
107 pars = GApplicationPars() 107 pars = GApplicationPars()
108 pars.append(GApplicationPar("outfile","f","h","sensitivity.dat","","","Output file name")) 108 pars.append(GApplicationPar("outfile","f","h","sensitivity.dat","","","Output file name"))
109 - pars.append(GApplicationPar("caldb","s","a","$GAMMALIB/share/caldb/cta","","","Calibration database")) 109 + pars.append(GApplicationPar("caldb","s","a","$CTOOLS/share/caldb/data/cta/dummy","","","Calibration database"))
110 pars.append(GApplicationPar("irf","s","a","cta_dummy_irf","","","Instrument response function")) 110 pars.append(GApplicationPar("irf","s","a","cta_dummy_irf","","","Instrument response function"))
111 pars.append(GApplicationPar("type","s","a","point","","","Source model type (point/gauss/shell/disk)")) 111 pars.append(GApplicationPar("type","s","a","point","","","Source model type (point/gauss/shell/disk)"))
112 pars.append(GApplicationPar("offset","r","a","0.0","0.0","","Source offset angle (deg)")) 112 pars.append(GApplicationPar("offset","r","a","0.0","0.0","","Source offset angle (deg)"))
113 - pars.append(GApplicationPar("bkg","s","a","$GAMMALIB/share/models/bkg_dummy.txt","","","Background model file function (none=power law for E)")) 113 + pars.append(GApplicationPar("bkg","s","a","$CTOOLS/share/models/bkg_dummy.txt","","","Background model file function (none=power law for E)"))
114 pars.append(GApplicationPar("duration","r","a","180000.0","","","Effective exposure time (s)")) 114 pars.append(GApplicationPar("duration","r","a","180000.0","","","Effective exposure time (s)"))
115 pars.append(GApplicationPar("rad","r","a","5.0","","","Radius of ROI (deg)")) 115 pars.append(GApplicationPar("rad","r","a","5.0","","","Radius of ROI (deg)"))
116 pars.append(GApplicationPar("enumbins","i","h","0","","","Number of energy bins (0=unbinned)")) 116 pars.append(GApplicationPar("enumbins","i","h","0","","","Number of energy bins (0=unbinned)"))
@@ -333,7 +333,7 @@ class cssens(GApplication): @@ -333,7 +333,7 @@ class cssens(GApplication):
333 location.lb_deg(l, b) 333 location.lb_deg(l, b)
334 334
335 # Set source spectrum 335 # Set source spectrum
336 - spectrum = GModelSpectralPlaw(flux*5.7e-16, index, GEnergy(0.3, "TeV") 336 + spectrum = GModelSpectralPlaw(flux*5.7e-16, index, GEnergy(0.3, "TeV"))
337 if fitidx: 337 if fitidx:
338 spectrum["Index"].free() 338 spectrum["Index"].free()
339 else: 339 else:
scripts/cta_kb_root2caldb.py 0 → 100755
  1 +#! /usr/bin/env python
  2 +# ==========================================================================
  3 +# This script generates CALDB compliant performance tables from MC ROOT
  4 +# files.
  5 +#
  6 +# Note that the effective area given in the ASCII file is the full effective
  7 +# area (i.e. the area before applying any theta cut). This is required by
  8 +# GammaLib as the maximum likelihood analysis does not imply any theta cut.
  9 +#
  10 +# Copyright (C) 2014 Juergen Knoedlseder
  11 +#
  12 +# This program is free software: you can redistribute it and/or modify
  13 +# it under the terms of the GNU General Public License as published by
  14 +# the Free Software Foundation, either version 3 of the License, or
  15 +# (at your option) any later version.
  16 +#
  17 +# This program is distributed in the hope that it will be useful,
  18 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
  19 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20 +# GNU General Public License for more details.
  21 +#
  22 +# You should have received a copy of the GNU General Public License
  23 +# along with this program. If not, see <http://www.gnu.org/licenses/>.
  24 +#
  25 +# ==========================================================================
  26 +from ROOT import TFile, TH1F, TH2F
  27 +from datetime import datetime
  28 +import gammalib
  29 +import math
  30 +import sys
  31 +import glob
  32 +import os
  33 +
  34 +
  35 +# =========== #
  36 +# CalDB class #
  37 +# =========== #
  38 +class caldb():
  39 + """
  40 + """
  41 + def __init__(self, inst, path=""):
  42 + """
  43 + CALDB constructor. The constructor will create one CALDB entry for
  44 + an observation.
  45 +
  46 + Parameters:
  47 + inst - Instrument string.
  48 + Keywords:
  49 + path - CALDB Root path.
  50 + """
  51 + # Store root path
  52 + self.path = path
  53 +
  54 + # Initialise some members
  55 + self.cif = None
  56 + self.irf = None
  57 + self.ea = None
  58 + self.psf = None
  59 + self.edisp = None
  60 + self.bgd = None
  61 + self.hdu_cif = None
  62 +
  63 + # Store UTC date of job execution
  64 + self.utc = datetime.utcnow().isoformat()[:19]
  65 +
  66 + # Set CALDB information
  67 + self.cal_tel = "CTA"
  68 + self.cal_inst = inst.upper()
  69 + self.cal_det = "NONE"
  70 + self.cal_flt = "NONE"
  71 + self.cal_class = "BCF"
  72 + self.cal_type = "DATA"
  73 + self.cal_qual = 0 # 0=good, 1=bad, 2=dubious, ...
  74 + self.cal_date = "14/01/01"
  75 + self.val_date = "2014-01-01"
  76 + self.val_time = "00:00:00"
  77 + self.ref_time = 51544.0
  78 + self.cal_name = "NAME()"
  79 + self.cal_version = "VERSION(Prod1)"
  80 + self.cal_cut = "CLASS(BEST)"
  81 + self.cal_analysis = "ANALYSIS(MPIK)"
  82 + #
  83 + self.ea_name = "EFF_AREA"
  84 + self.ea_doc = "???"
  85 + self.ea_bounds = [self.cal_name, self.cal_version, self.cal_cut, self.cal_analysis]
  86 + self.ea_desc = "CTA effective area"
  87 + #
  88 + self.psf_name = "RPSF"
  89 + self.psf_doc = "???"
  90 + self.psf_bounds = [self.cal_name, self.cal_version, self.cal_cut, self.cal_analysis]
  91 + self.psf_desc = "CTA point spread function"
  92 + #
  93 + self.edisp_name = "EDISP"
  94 + self.edisp_doc = "???"
  95 + self.edisp_bounds = [self.cal_name, self.cal_version, self.cal_cut, self.cal_analysis]
  96 + self.edisp_desc = "CTA energy dispersion"
  97 + #
  98 + self.bgd_name = "BGD"
  99 + self.bgd_doc = "???"
  100 + self.bgd_bounds = [self.cal_name, self.cal_version, self.cal_cut, self.cal_analysis]
  101 + self.bgd_desc = "CTA background"
  102 +
  103 + # Create directory structure
  104 + self.make_dirs()
  105 +
  106 + # Return
  107 + return
  108 +
  109 + def __del__(self):
  110 + """
  111 + Destructor. Close all FITS files.
  112 + """
  113 + # Close calibration
  114 + self.close()
  115 +
  116 + # Return
  117 + return
  118 +
  119 + def make_dirs(self):
  120 + """
  121 + Generate CALDB directory structure for one observation identifier.
  122 + The structure is given by
  123 +
  124 + data/<tel>/<inst>/bcf/
  125 +
  126 + where <tel> is "cta" and <inst> is the instrument specified in the
  127 + CALDB constructor (the instrument may be used for different array
  128 + configurations).
  129 +
  130 + Parameters:
  131 + None
  132 + Keywords:
  133 + None
  134 + """
  135 + # Set base path
  136 + self.base_dir = "data"
  137 + self.base_dir += "/"+self.cal_tel.lower()
  138 + self.base_dir += "/"+self.cal_inst.lower()
  139 +
  140 + # Set directory names
  141 + self.bcf_dir = self.base_dir+"/bcf"
  142 +
  143 + # Optionally prefix path
  144 + if len(self.path) > 0:
  145 + self.base_path = self.path+"/"+self.base_dir
  146 + self.bcf_path = self.path+"/"+self.bcf_dir
  147 + else:
  148 + self.base_path = self.base_dir
  149 + self.bcf_path = self.bcf_dir
  150 +
  151 + # Create directory structure
  152 + if not os.path.isdir(self.bcf_path):
  153 + os.makedirs(self.bcf_path)
  154 +
  155 + # Return
  156 + return
  157 +
  158 + def add(self, rspname, split=False, clobber=True):
  159 + """
  160 + Add new calibration. The actual version will put
  161 + all calibrations in the same file, although each part of the response
  162 + function will have its own logical name. We can thus easily modify
  163 + the script to put each calibration information in a separate file.
  164 +
  165 + Parameters:
  166 + rspname - Response name
  167 + Keywords:
  168 + split - Split IRF over several files?
  169 + clobber - Overwrite existing files?
  170 + """
  171 + # Set calibrate file names
  172 + if split:
  173 + self.ea_file = "ea_"+rspname+".dat"
  174 + self.psf_file = "psf_"+rspname+".dat"
  175 + self.edisp_file = "edisp_"+rspname+".dat"
  176 + self.bgd_file = "bgd_"+rspname+".dat"
  177 + else:
  178 + self.ea_file = "irf_"+rspname+".dat"
  179 + self.psf_file = "irf_"+rspname+".dat"
  180 + self.edisp_file = "irf_"+rspname+".dat"
  181 + self.bgd_file = "irf_"+rspname+".dat"
  182 +
  183 + # Open calibration database index
  184 + if self.cif == None:
  185 + self.cif = gammalib.GFits(self.base_path+"/caldb.indx", True)
  186 +
  187 + # If file has no CIF extension than create it now
  188 + try:
  189 + self.hdu_cif = self.cif.table("CIF")
  190 + except:
  191 + self.create_cif()
  192 + self.hdu_cif = self.cif.table("CIF")
  193 +
  194 + # Set response name
  195 + self.cal_name = "NAME("+rspname+")"
  196 +
  197 + # Return
  198 + return
  199 +
  200 + def close(self):
  201 + """
  202 + Close calibration FITS files.
  203 +
  204 + Parameters:
  205 + None
  206 + Keywords:
  207 + None
  208 + """
  209 + # Add information to CIF. We do this now as before we did not
  210 + # necessarily have all the information at hand (in particular
  211 + # about the boundaries)
  212 + #self.add_cif_info()
  213 +
  214 + # Close CIF
  215 + if self.cif != None:
  216 + self.cif.save(True)
  217 + self.cif.close()
  218 + self.cif = None
  219 +
  220 + # Free
  221 + self.cif = None
  222 +
  223 + # Return
  224 + return
  225 +
  226 + def create_cif(self):
  227 + """
  228 + Create Calibration Index File (CIF) extension in CIF FITS file.
  229 +
  230 + Parameters:
  231 + None
  232 + Keywords:
  233 + None
  234 + """
  235 + # Create binary table
  236 + table = gammalib.GFitsBinTable()
  237 +
  238 + # Set boundary
  239 +
  240 + # Attach columns. Reference: CAL/GEN/92-008
  241 + table.append(gammalib.GFitsTableStringCol("TELESCOP", 0, 10))
  242 + table.append(gammalib.GFitsTableStringCol("INSTRUME", 0, 10))
  243 + table.append(gammalib.GFitsTableStringCol("DETNAM", 0, 20))
  244 + table.append(gammalib.GFitsTableStringCol("FILTER", 0, 10))
  245 + table.append(gammalib.GFitsTableStringCol("CAL_DEV", 0, 20))
  246 + table.append(gammalib.GFitsTableStringCol("CAL_DIR", 0, 70))
  247 + table.append(gammalib.GFitsTableStringCol("CAL_FILE", 0, 40))
  248 + table.append(gammalib.GFitsTableStringCol("CAL_CLAS", 0, 3))
  249 + table.append(gammalib.GFitsTableStringCol("CAL_DTYP", 0, 4))
  250 + table.append(gammalib.GFitsTableStringCol("CAL_CNAM", 0, 20))
  251 + table.append(gammalib.GFitsTableStringCol("CAL_CBD", 0, 70, 9))
  252 + table.append(gammalib.GFitsTableShortCol("CAL_XNO", 0))
  253 + table.append(gammalib.GFitsTableStringCol("CAL_VSD", 0, 10))
  254 + table.append(gammalib.GFitsTableStringCol("CAL_VST", 0, 8))
  255 + table.append(gammalib.GFitsTableDoubleCol("REF_TIME", 0))
  256 + table.append(gammalib.GFitsTableShortCol("CAL_QUAL", 0))
  257 + table.append(gammalib.GFitsTableStringCol("CAL_DATE", 0, 8))
  258 + table.append(gammalib.GFitsTableStringCol("CAL_DESC", 0, 70))
  259 +
  260 + # Set keywords. Reference: CAL/GEN/92-008
  261 + table.extname("CIF")
  262 + table.card("CIFVERSN", "1992a", "Version of CIF format")
  263 +
  264 + # Attach table to FITS file. Note that at this moment the
  265 + # FITS table gets connected to the FITS file. Yet since
  266 + # nothing was yet written to the FITS file, we cannot read
  267 + # anything from it.
  268 + self.cif.append(table)
  269 +
  270 + # Return
  271 + return
  272 +
  273 + def add_cif_info(self):
  274 + """
  275 + Add information to CIF extension.
  276 +
  277 + Parameters:
  278 + None
  279 + Keywords:
  280 + None
  281 + """
  282 + # Set boundaries
  283 + self.ea_bounds = [self.cal_name, self.cal_version, self.cal_cut, self.cal_analysis]
  284 + self.psf_bounds = [self.cal_name, self.cal_version, self.cal_cut, self.cal_analysis]
  285 + self.edisp_bounds = [self.cal_name, self.cal_version, self.cal_cut, self.cal_analysis]
  286 + self.bgd_bounds = [self.cal_name, self.cal_version, self.cal_cut, self.cal_analysis]
  287 +
  288 + # Append 4 rows to CIF extension
  289 + self.hdu_cif.append_rows(4)
  290 +
  291 + # Add generic information for these 4 rows
  292 + for i in range(4):
  293 +
  294 + # Set row number
  295 + row = i+self.hdu_cif.nrows()-4
  296 +
  297 + # Set element
  298 + self.hdu_cif["TELESCOP"][row] = self.cal_tel
  299 + self.hdu_cif["INSTRUME"][row] = self.cal_inst
  300 + self.hdu_cif["DETNAM"][row] = self.cal_det
  301 + self.hdu_cif["FILTER"][row] = self.cal_flt
  302 + self.hdu_cif["CAL_DEV"][row] = "ONLINE"
  303 + self.hdu_cif["CAL_CLAS"][row] = self.cal_class
  304 + self.hdu_cif["CAL_DTYP"][row] = self.cal_type
  305 + self.hdu_cif["CAL_VSD"][row] = self.val_date
  306 + self.hdu_cif["CAL_VST"][row] = self.val_time
  307 + self.hdu_cif["REF_TIME"][row] = self.ref_time
  308 + self.hdu_cif["CAL_QUAL"][row] = self.cal_qual
  309 + self.hdu_cif["CAL_DATE"][row] = self.cal_date
  310 +
  311 + # Add effective area information
  312 + row = self.hdu_cif.nrows()-4
  313 + self.hdu_cif["CAL_DIR"][row] = self.bcf_dir
  314 + self.hdu_cif["CAL_FILE"][row] = self.ea_file
  315 + self.hdu_cif["CAL_CNAM"][row] = self.ea_name
  316 + self.hdu_cif["CAL_DESC"][row] = self.ea_desc
  317 + self.hdu_cif["CAL_XNO"][row] = 1
  318 + for n in range(9):
  319 + if n >= len(self.ea_bounds):
  320 + self.hdu_cif["CAL_CBD"][row, n] = "NONE"
  321 + else:
  322 + self.hdu_cif["CAL_CBD"][row, n] = self.ea_bounds[n]
  323 +
  324 + # Add point spread function information
  325 + row = self.hdu_cif.nrows()-3
  326 + self.hdu_cif["CAL_DIR"][row] = self.bcf_dir
  327 + self.hdu_cif["CAL_FILE"][row] = self.psf_file
  328 + self.hdu_cif["CAL_CNAM"][row] = self.psf_name
  329 + self.hdu_cif["CAL_DESC"][row] = self.psf_desc
  330 + self.hdu_cif["CAL_XNO"][row] = 1
  331 + for n in range(9):
  332 + if n >= len(self.psf_bounds):
  333 + self.hdu_cif["CAL_CBD"][row, n] = "NONE"
  334 + else:
  335 + self.hdu_cif["CAL_CBD"][row, n] = self.psf_bounds[n]
  336 +
  337 + # Add energy dispersion information
  338 + row = self.hdu_cif.nrows()-2
  339 + self.hdu_cif["CAL_DIR"][row] = self.bcf_dir
  340 + self.hdu_cif["CAL_FILE"][row] = self.edisp_file
  341 + self.hdu_cif["CAL_CNAM"][row] = self.edisp_name
  342 + self.hdu_cif["CAL_DESC"][row] = self.edisp_desc
  343 + self.hdu_cif["CAL_XNO"][row] = 1
  344 + for n in range(9):
  345 + if n >= len(self.edisp_bounds):
  346 + self.hdu_cif["CAL_CBD"][row, n] = "NONE"
  347 + else:
  348 + self.hdu_cif["CAL_CBD"][row, n] = self.edisp_bounds[n]
  349 +
  350 + # Add background information
  351 + row = self.hdu_cif.nrows()-1
  352 + self.hdu_cif["CAL_DIR"][row] = self.bcf_dir
  353 + self.hdu_cif["CAL_FILE"][row] = self.bgd_file
  354 + self.hdu_cif["CAL_CNAM"][row] = self.bgd_name
  355 + self.hdu_cif["CAL_DESC"][row] = self.bgd_desc
  356 + self.hdu_cif["CAL_XNO"][row] = 1
  357 + for n in range(9):
  358 + if n >= len(self.bgd_bounds):
  359 + self.hdu_cif["CAL_CBD"][row, n] = "NONE"
  360 + else:
  361 + self.hdu_cif["CAL_CBD"][row, n] = self.bgd_bounds[n]
  362 +
  363 + # Return
  364 + return
  365 +
  366 + def root2caldb(self, filename):
  367 + """
  368 + Generate files.
  369 +
  370 + Parameters:
  371 + filename - Performance file name
  372 + """
  373 + # Open ROOT performance file
  374 + rootfile = TFile(filename)
  375 +
  376 + # Set filenames
  377 + ea_filename = self.bcf_path+"/"+self.ea_file
  378 + psf_filename = self.bcf_path+"/"+self.psf_file
  379 + edisp_filename = self.bcf_path+"/"+self.edisp_file
  380 + bgd_filename = self.bcf_path+"/"+self.bgd_file
  381 +
  382 + # Open ASCII performance tables
  383 + if self.ea_file == self.psf_file and \
  384 + self.ea_file == self.edisp_file and \
  385 + self.ea_file == self.bgd_file :
  386 + split = False
  387 + fprf = open(ea_filename, "w")
  388 + files = [fprf]
  389 + else:
  390 + split = True
  391 + fea = open(ea_filename, "w")
  392 + fpsf = open(psf_filename, "w")
  393 + fedisp = open(edisp_filename, "w")
  394 + fbgd = open(bgd_filename, "w")
  395 + files = [fea, fpsf, fedisp, fbgd]
  396 +
  397 + # Write header
  398 + for file in files:
  399 + file.write("log(E) Area r68 r80 ERes. BG Rate Diff Sens\n")
  400 +
  401 + # Get histograms. Konrad's root files do not have an EffectiveArea80
  402 + # histogram, but his effective area is for 80% containment radius
  403 + # anyways, so we simply read this histogram into aeff80.
  404 + sens = TH1F()
  405 + bgrate = TH1F()
  406 + bgratesqdeg = TH1F()
  407 + aeff = TH1F()
  408 + aeff80 = TH1F()
  409 + angres = TH1F()
  410 + angres80 = TH1F()
  411 + eres = TH1F()
  412 + rootfile.GetObject("DiffSens", sens)
  413 + rootfile.GetObject("BGRate", bgrate)
  414 + rootfile.GetObject("BGRatePerSqDeg", bgratesqdeg)
  415 + rootfile.GetObject("EffectiveArea", aeff)
  416 + try:
  417 + rootfile.GetObject("EffectiveArea80", aeff80)
  418 + except:
  419 + rootfile.GetObject("EffectiveArea", aeff80)
  420 + rootfile.GetObject("AngRes", angres)
  421 + rootfile.GetObject("AngRes80", angres80)
  422 + rootfile.GetObject("ERes", eres)
  423 +
  424 + # Setup vectors
  425 + energies = bgrate.GetXaxis()
  426 + nbins_eng = energies.GetNbins()
  427 + for i in range(nbins_eng):
  428 +
  429 + # Get logE and background rate per squared degree
  430 + logE = energies.GetBinCenter(i+1)
  431 + bgd = bgrate.GetBinContent(i+1,1)
  432 + bgdsq = bgratesqdeg.GetBinContent(i+1,1)
  433 + area = aeff.GetBinContent(i+1,1)
  434 + area80 = aeff80.GetBinContent(i+1,1)
  435 + r68 = angres.GetBinContent(i+1,1)
  436 + r80 = angres80.GetBinContent(i+1,1)
  437 + dE = eres.GetBinContent(i+1,1)
  438 + diffs = sens.GetBinContent(i+1,1)
  439 +
  440 + # Skip any NaNs
  441 + if math.isnan(area80):
  442 + continue
  443 +
  444 + # Compute energy (in MeV)
  445 + energy = math.pow(10.0, logE)*1.0e6
  446 + emin = math.pow(10.0, logE-0.1)*1.0e6
  447 + emax = math.pow(10.0, logE+0.1)*1.0e6
  448 + ewidth = emax-emin
  449 +
  450 + # Convert into background rate per steradian and MeV
  451 + bkg_rate = bgdsq / (0.01745329*0.01745329) / ewidth
  452 +
  453 + # Compute control background rate (this only works for KB files
  454 + # as they are for 80% containment radius). But we don't use this
  455 + # anyways, it's just for control on display ...
  456 + omega = 2.0 * math.pi * (1.0 - math.cos(math.radians(r80)))
  457 + if omega > 0.0:
  458 + bkg_rate2 = bgd / omega / ewidth
  459 + else:
  460 + bkg_rate2 = 0.0
  461 +
  462 + # Compute full effective area by dividing area80/0.80
  463 + area = area80/0.80
  464 +
  465 + # Write results in file
  466 + line = "%.1f %.1f %.4f %.4f %.4f %.5e %.5e" % \
  467 + (logE, area, r68, r80, dE, bgd, diffs)
  468 + for file in files:
  469 + file.write(line+"\n")
  470 + #fbkg.write(str(energy)+" "+str(bkg_rate)+"\n")
  471 +
  472 + # Write trailer
  473 + for file in files:
  474 + file.write("---------------------------------------------\n")
  475 + file.write("Notes\n")
  476 + file.write(" 1) log(E) = log10(E/TeV) - bin centre\n")
  477 + file.write(" 2) Eff Area - in square metres after background cut (no theta cut)\n")
  478 + file.write(" 3) Ang. Res - 68% containment radius of gamma-ray PSF post cuts - in degrees\n")
  479 + file.write(" 4) Ang. Res - 80% containment radius of gamma-ray PSF post cuts - in degrees\n")
  480 + file.write(" 5) Fractional Energy Resolution (rms)\n")
  481 + file.write(" 6) BG Rate - inside point-source selection region - post call cuts - in Hz\n")
  482 + file.write(" 7) Diff Sens - differential sensitivity for this bin expressed as E^2 dN/dE\n")
  483 + file.write(" - in erg cm^-2 s^-1 - for a 50 hours exposure - 5 sigma significance including\n")
  484 + file.write(" systematics and statistics and at least 10 photons.\n")
  485 +
  486 + # Close files
  487 + for file in files:
  488 + file.close()
  489 +
  490 + # Close ROOT performance file
  491 + rootfile.Close()
  492 +
  493 + # Add information to CIF. We do this now as before we did not
  494 + # necessarily have all the information at hand (in particular
  495 + # about the boundaries)
  496 + self.add_cif_info()
  497 +
  498 + # Return
  499 + return
  500 +
  501 +
  502 +#==========================#
  503 +# Main routine entry point #
  504 +#==========================#
  505 +if __name__ == '__main__':
  506 + """
  507 + """
  508 + # Get ROOT filename from command line argument
  509 + if (len(sys.argv) > 1):
  510 + filename = sys.argv[1]
  511 + else:
  512 + db = "/Users/jurgen/Documents/Travail/Projects/CTA/WP-MC/root/Kb_all_20deg_root"
  513 + #db = "/Users/jurgen/Documents/Travail/Projects/CTA/WP-MC/root/Kb_all_50deg_root"
  514 +
  515 + # Get list of all performance files
  516 + files = glob.glob(db+"/*.root")
  517 + files.sort()
  518 +
  519 + # Allocate caldb
  520 + irf = caldb("kb")
  521 +
  522 + # Loop over all files
  523 + for file in files:
  524 +
  525 + # Get filename
  526 + head, tail = os.path.split(file)
  527 +
  528 + # Build name
  529 + name = tail.strip(".root")
  530 + if name.find("kb_") != -1:
  531 + name = name.replace("_20deg", "")
  532 + elif name.find("IFAE_") != -1:
  533 + name = name.replace("Subarray", "")
  534 + name = name.replace("IFAE_", "")
  535 + name = "IFAE_"+name
  536 + i = name.find("hours")
  537 + if i != -1:
  538 + name = name[0:i+1]
  539 + print(tail)
  540 +
  541 + # Open calibration file
  542 + irf.add(name)
  543 +
  544 + # Translate ROOT to CALDB information
  545 + irf.root2caldb(file)
scripts/cta_root2caldb.py
@@ -1319,9 +1319,9 @@ def set_prod1_ifae(): @@ -1319,9 +1319,9 @@ def set_prod1_ifae():
1319 #=========================# 1319 #=========================#
1320 # Set Prod2 DESY database # 1320 # Set Prod2 DESY database #
1321 #=========================# 1321 #=========================#
1322 -def set_prod2_desy(): 1322 +def set_prod2_desy_ignore():
1323 """ 1323 """
1324 - Set Prod2 DESY database. 1324 + Set Prod2 DESY database (ignore these files).
1325 """ 1325 """
1326 # Set database attributes 1326 # Set database attributes
1327 path_aar = "/project-data/cta/performance/prod2/Performance_DESY_20140131_Aar" 1327 path_aar = "/project-data/cta/performance/prod2/Performance_DESY_20140131_Aar"
@@ -1366,6 +1366,56 @@ def set_prod2_desy(): @@ -1366,6 +1366,56 @@ def set_prod2_desy():
1366 return db 1366 return db
1367 1367
1368 1368
  1369 +#=========================#
  1370 +# Set Prod2 DESY database #
  1371 +#=========================#
  1372 +def set_prod2_desy():
  1373 + """
  1374 + Set Prod2 DESY database.
  1375 + """
  1376 + # Set database attributes
  1377 + path_aar = "/project-data/cta/performance/prod2/Performance_DESY_20140131_Aar"
  1378 + path_ten = "/project-data/cta/performance/prod2/Performance_DESY_20140131_Tenerife_50h"
  1379 + rebin = False
  1380 + psftype = "Gauss"
  1381 + scale = 1.0
  1382 +
  1383 + # Set database content
  1384 + db = [{'inst': "aar", 'id': "DESY20140105_50h",
  1385 + 'path': path_aar, 'rebin': rebin, 'psftype': psftype, 'scale': scale,
  1386 + 'file': "DESY.d20140105.Erec1.V3.ID0NIM2.prod2-Aar-NS.S.2a.180000s.root"},
  1387 + {'inst': "aar", 'id': "DESY20140105_50h_0deg",
  1388 + 'path': path_aar, 'rebin': rebin, 'psftype': psftype, 'scale': scale,
  1389 + 'file': "DESY.d20140105.Erec1.V3.ID0_0degNIM2.prod2-Aar-NS.S.2a.180000s.root"},
  1390 + {'inst': "aar", 'id': "DESY20140105_50h_180deg",
  1391 + 'path': path_aar, 'rebin': rebin, 'psftype': psftype, 'scale': scale,
  1392 + 'file': "DESY.d20140105.Erec1.V3.ID0_180degNIM2.prod2-Aar-NS.S.2a.180000s.root"},
  1393 +
  1394 + {'inst': "aar500", 'id': "DESY20140105_50h",
  1395 + 'path': path_aar, 'rebin': rebin, 'psftype': psftype, 'scale': scale,
  1396 + 'file': "DESY.d20140105.Erec1.V3.ID0NIM2.prod2-Aar-500m-NS.S.2a.180000s.root"},
  1397 + {'inst': "aar500", 'id': "DESY20140105_50h_0deg",
  1398 + 'path': path_aar, 'rebin': rebin, 'psftype': psftype, 'scale': scale,
  1399 + 'file': "DESY.d20140105.Erec1.V3.ID0_0degNIM2.prod2-Aar-NS.S.2a.180000s.root"},
  1400 + {'inst': "aar500", 'id': "DESY20140105_50h_180deg",
  1401 + 'path': path_aar, 'rebin': rebin, 'psftype': psftype, 'scale': scale,
  1402 + 'file': "DESY.d20140105.Erec1.V3.ID0_180degNIM2.prod2-Aar-NS.S.2a.180000s.root"},
  1403 +
  1404 + {'inst': "tenerife", 'id': "DESY20140105_50h",
  1405 + 'path': path_ten, 'rebin': rebin, 'psftype': psftype, 'scale': scale,
  1406 + 'file': "DESY.d20140105.Erec1.V3.ID0NIM2.prod2-Tenerife-NS.N.2NN.180000s.root"},
  1407 + {'inst': "tenerife", 'id': "DESY20140105_50h_0deg",
  1408 + 'path': path_ten, 'rebin': rebin, 'psftype': psftype, 'scale': scale,
  1409 + 'file': "DESY.d20140105.Erec1.V3.ID0_0degNIM2.prod2-Tenerife-NS.N.2NN.180000s.root"},
  1410 + {'inst': "tenerife", 'id': "DESY20140105_50h_180deg",
  1411 + 'path': path_ten, 'rebin': rebin, 'psftype': psftype, 'scale': scale,
  1412 + 'file': "DESY.d20140105.Erec1.V3.ID0_180degNIM2.prod2-Tenerife-NS.N.2NN.180000s.root"}
  1413 + ]
  1414 +
  1415 + # Return database
  1416 + return db
  1417 +
  1418 +
1369 #==========================# 1419 #==========================#
1370 # Main routine entry point # 1420 # Main routine entry point #
1371 #==========================# 1421 #==========================#
@@ -1381,7 +1431,7 @@ if __name__ == &#39;__main__&#39;: @@ -1381,7 +1431,7 @@ if __name__ == &#39;__main__&#39;:
1381 - EffectiveArea80_offaxis (2D) - Effective area for 80% source containment 1431 - EffectiveArea80_offaxis (2D) - Effective area for 80% source containment
1382 - AngRes_offaxis (2D) - Angular resolution 68% containment 1432 - AngRes_offaxis (2D) - Angular resolution 68% containment
1383 - AngRes80_offaxis (2D) - Angular resolution 80% containment 1433 - AngRes80_offaxis (2D) - Angular resolution 80% containment
1384 - - BGRatePerSqDeg_offaxis (2D) - Background rate per square degree 1434 + - BGRatePerSqDeg_offaxis (2D) - Background rate per square degree [counts/s/deg2]
1385 - BGRate_offaxis (2D) - Background rate 1435 - BGRate_offaxis (2D) - Background rate
1386 - EffectiveAreaEtrue_offaxis (2D) - Effective area in true energy (finer bins) 1436 - EffectiveAreaEtrue_offaxis (2D) - Effective area in true energy (finer bins)
1387 - MigMatrix_offaxis (3D) 1437 - MigMatrix_offaxis (3D)
scripts/cta_root2irf.py
@@ -38,58 +38,58 @@ import os @@ -38,58 +38,58 @@ import os
38 # Compute Eq. (17) of Li & Ma (1983) # 38 # Compute Eq. (17) of Li & Ma (1983) #
39 # ================================== # 39 # ================================== #
40 def sigma_lima(Non, Noff, alpha=0.2): 40 def sigma_lima(Non, Noff, alpha=0.2):
41 - """  
42 - Compute Eq. (17) of Li & Ma (1983).  
43 -  
44 - Parameters:  
45 - Non - Number of on counts  
46 - Noff - Number of off counts  
47 - Keywords:  
48 - alpha - Ratio of on-to-off exposure  
49 - """  
50 - # Compute sensitivity  
51 - alpha1 = alpha + 1.0  
52 - sum = Non + Noff  
53 - arg1 = Non / sum  
54 - arg2 = Noff / sum  
55 - term1 = Non * math.log((alpha1/alpha)*arg1)  
56 - term2 = Noff * math.log(alpha1*arg2)  
57 - sigma = math.sqrt(2.0 * (term1 + term2))  
58 -  
59 - # Return sensitivity  
60 - return sigma 41 + """
  42 + Compute Eq. (17) of Li & Ma (1983).
  43 +
  44 + Parameters:
  45 + Non - Number of on counts
  46 + Noff - Number of off counts
  47 + Keywords:
  48 + alpha - Ratio of on-to-off exposure
  49 + """
  50 + # Compute sensitivity
  51 + alpha1 = alpha + 1.0
  52 + sum = Non + Noff
  53 + arg1 = Non / sum
  54 + arg2 = Noff / sum
  55 + term1 = Non * math.log((alpha1/alpha)*arg1)
  56 + term2 = Noff * math.log(alpha1*arg2)
  57 + sigma = math.sqrt(2.0 * (term1 + term2))
  58 +
  59 + # Return sensitivity
  60 + return sigma
61 61
62 62
63 # ======================================== # 63 # ======================================== #
64 # Solve Eq. (17) of Li & Ma (1983) for Non # 64 # Solve Eq. (17) of Li & Ma (1983) for Non #
65 # ======================================== # 65 # ======================================== #
66 def Non_lima(sigma, Noff, alpha=0.2): 66 def Non_lima(sigma, Noff, alpha=0.2):
67 - """  
68 - Solve Eq. (17) of Li & Ma (1983) for Non  
69 -  
70 - Parameters:  
71 - sigma - Required significance  
72 - Noff - Number of off counts  
73 - Keywords:  
74 - alpha - Ratio of on-to-off exposure  
75 - """  
76 - # Set initial guess for Non  
77 - S = 25.0  
78 - Non = S + alpha*Noff  
79 -  
80 - # Compute initial sigma estimate  
81 - s = sigma_lima(Non, Noff, alpha=alpha)  
82 - delta = s - sigma  
83 -  
84 - # Iterate  
85 - while abs(delta) > 1.0e-6:  
86 - S *= sigma/s  
87 - Non = S + alpha*Noff  
88 - s = sigma_lima(Non, Noff, alpha=alpha)  
89 - delta = s - sigma  
90 -  
91 - # Return  
92 - return Non 67 + """
  68 + Solve Eq. (17) of Li & Ma (1983) for Non
  69 +
  70 + Parameters:
  71 + sigma - Required significance
  72 + Noff - Number of off counts
  73 + Keywords:
  74 + alpha - Ratio of on-to-off exposure
  75 + """
  76 + # Set initial guess for Non
  77 + S = 25.0
  78 + Non = S + alpha*Noff
  79 +
  80 + # Compute initial sigma estimate
  81 + s = sigma_lima(Non, Noff, alpha=alpha)
  82 + delta = s - sigma
  83 +
  84 + # Iterate
  85 + while abs(delta) > 1.0e-6:
  86 + S *= sigma/s
  87 + Non = S + alpha*Noff
  88 + s = sigma_lima(Non, Noff, alpha=alpha)
  89 + delta = s - sigma
  90 +
  91 + # Return
  92 + return Non
93 93
94 94
95 # =============================================== # 95 # =============================================== #
@@ -97,195 +97,195 @@ def Non_lima(sigma, Noff, alpha=0.2): @@ -97,195 +97,195 @@ def Non_lima(sigma, Noff, alpha=0.2):
97 # =============================================== # 97 # =============================================== #
98 def get_aeff(bgrate, emin, emax, \ 98 def get_aeff(bgrate, emin, emax, \
99 duration=180000.0, alpha=0.2, sigma=5.0, norm=2.79e-7, index=-2.57): 99 duration=180000.0, alpha=0.2, sigma=5.0, norm=2.79e-7, index=-2.57):
100 - """  
101 - Computes effective area assuming a Crab spectrum. This does not  
102 - include any hadron cut efficiencies, and thus is not very useful in a first  
103 - place ...  
104 -  
105 - Parameters:  
106 - bgrate - Background rate (TeV)  
107 - emin - Minimum energy (TeV)  
108 - emax - Maximum energy (TeV)  
109 - Keywords:  
110 - duration - Observing time in seconds (default: 50h)  
111 - alpha - Non/Noff ratio (default: 0.2)  
112 - sigma - Significance (default: 5.0)  
113 - norm - Crab spectrum normalization at 1 TeV (default: 2.79e-7)  
114 - index - Crab spectral index (default: -2.57)  
115 - """  
116 - # Compute number of source events  
117 - B = bgrate*duration  
118 - Noff = B/alpha  
119 - Non = Non_lima(sigma, Noff)  
120 - S = (Non - B)  
121 -  
122 - # Compute Crab flux in specified energy band  
123 - inx = index+1.0  
124 - cu = norm/inx * (math.pow(emax, inx) - math.pow(emin, inx))  
125 -  
126 - # Compute effective area  
127 - area = S / duration / cu  
128 -  
129 - # Return effective area  
130 - return area 100 + """
  101 + Computes effective area assuming a Crab spectrum. This does not
  102 + include any hadron cut efficiencies, and thus is not very useful in a first
  103 + place ...
  104 +
  105 + Parameters:
  106 + bgrate - Background rate (TeV)
  107 + emin - Minimum energy (TeV)
  108 + emax - Maximum energy (TeV)
  109 + Keywords:
  110 + duration - Observing time in seconds (default: 50h)
  111 + alpha - Non/Noff ratio (default: 0.2)
  112 + sigma - Significance (default: 5.0)
  113 + norm - Crab spectrum normalization at 1 TeV (default: 2.79e-7)
  114 + index - Crab spectral index (default: -2.57)
  115 + """
  116 + # Compute number of source events
  117 + B = bgrate*duration
  118 + Noff = B/alpha
  119 + Non = Non_lima(sigma, Noff)
  120 + S = (Non - B)
  121 +
  122 + # Compute Crab flux in specified energy band
  123 + inx = index+1.0
  124 + cu = norm/inx * (math.pow(emax, inx) - math.pow(emin, inx))
  125 +
  126 + # Compute effective area
  127 + area = S / duration / cu
  128 +
  129 + # Return effective area
  130 + return area
131 131
132 132
133 # ============== # 133 # ============== #
134 # Generate files # 134 # Generate files #
135 # ============== # 135 # ============== #
136 def make_files(filename, prfname, bkgname): 136 def make_files(filename, prfname, bkgname):
137 - """  
138 - Generate files.  
139 -  
140 - Parameters:  
141 - filename - Performance file name  
142 - prfname - Performance file name  
143 - bkgname - File function filename  
144 - """  
145 - # Open performance file and file function  
146 - file = TFile(filename)  
147 - fprf = open(prfname, "w")  
148 - fbkg = open(bkgname, "w")  
149 -  
150 - # Write header  
151 - fprf.write("log(E) Area r68 r80 ERes. BG Rate Diff Sens\n")  
152 - #sys.stdout.write("log(E) Area Area80 r68 r80 ERes. BG Rate Diff Sens BGRateSqDeg BGInt (BGIntControl)\n") 137 + """
  138 + Generate files.
  139 +
  140 + Parameters:
  141 + filename - Performance file name
  142 + prfname - Performance file name
  143 + bkgname - File function filename
  144 + """
  145 + # Open performance file and file function
  146 + file = TFile(filename)
  147 + fprf = open(prfname, "w")
  148 + fbkg = open(bkgname, "w")
  149 +
  150 + # Write header
  151 + fprf.write("log(E) Area r68 r80 ERes. BG Rate Diff Sens\n")
  152 + #sys.stdout.write("log(E) Area Area80 r68 r80 ERes. BG Rate Diff Sens BGRateSqDeg BGInt (BGIntControl)\n")
153 153
154 - # Get histograms. Konrad's root files do not have an EffectiveArea80  
155 - # histogram, but his effective area is for 80% containment radius  
156 - # anyways, so we simply read this histogram into aeff80.  
157 - sens = TH1F()  
158 - bgrate = TH1F()  
159 - bgratesqdeg = TH1F()  
160 - aeff = TH1F()  
161 - aeff80 = TH1F()  
162 - angres = TH1F()  
163 - angres80 = TH1F()  
164 - eres = TH1F()  
165 - file.GetObject("DiffSens", sens)  
166 - file.GetObject("BGRate", bgrate)  
167 - file.GetObject("BGRatePerSqDeg", bgratesqdeg)  
168 - file.GetObject("EffectiveArea", aeff)  
169 - try:  
170 - file.GetObject("EffectiveArea80", aeff80)  
171 - except:  
172 - file.GetObject("EffectiveArea", aeff80)  
173 - file.GetObject("AngRes", angres)  
174 - file.GetObject("AngRes80", angres80)  
175 - file.GetObject("ERes", eres)  
176 -  
177 - # Setup vectors  
178 - energies = bgrate.GetXaxis()  
179 - nbins_eng = energies.GetNbins()  
180 - for i in range(nbins_eng):  
181 -  
182 - # Get logE and background rate per squared degree  
183 - logE = energies.GetBinCenter(i+1)  
184 - bgd = bgrate.GetBinContent(i+1,1)  
185 - bgdsq = bgratesqdeg.GetBinContent(i+1,1)  
186 - area = aeff.GetBinContent(i+1,1)  
187 - area80 = aeff80.GetBinContent(i+1,1)  
188 - r68 = angres.GetBinContent(i+1,1)  
189 - r80 = angres80.GetBinContent(i+1,1)  
190 - dE = eres.GetBinContent(i+1,1)  
191 - diffs = sens.GetBinContent(i+1,1)  
192 -  
193 - # Skip any NaNs  
194 - if math.isnan(area80):  
195 - continue 154 + # Get histograms. Konrad's root files do not have an EffectiveArea80
  155 + # histogram, but his effective area is for 80% containment radius
  156 + # anyways, so we simply read this histogram into aeff80.
  157 + sens = TH1F()
  158 + bgrate = TH1F()
  159 + bgratesqdeg = TH1F()
  160 + aeff = TH1F()
  161 + aeff80 = TH1F()
  162 + angres = TH1F()
  163 + angres80 = TH1F()
  164 + eres = TH1F()
  165 + file.GetObject("DiffSens", sens)
  166 + file.GetObject("BGRate", bgrate)
  167 + file.GetObject("BGRatePerSqDeg", bgratesqdeg)
  168 + file.GetObject("EffectiveArea", aeff)
  169 + try:
  170 + file.GetObject("EffectiveArea80", aeff80)
  171 + except:
  172 + file.GetObject("EffectiveArea", aeff80)
  173 + file.GetObject("AngRes", angres)
  174 + file.GetObject("AngRes80", angres80)
  175 + file.GetObject("ERes", eres)
  176 +
  177 + # Setup vectors
  178 + energies = bgrate.GetXaxis()
  179 + nbins_eng = energies.GetNbins()
  180 + for i in range(nbins_eng):
  181 +
  182 + # Get logE and background rate per squared degree
  183 + logE = energies.GetBinCenter(i+1)
  184 + bgd = bgrate.GetBinContent(i+1,1)
  185 + bgdsq = bgratesqdeg.GetBinContent(i+1,1)
  186 + area = aeff.GetBinContent(i+1,1)
  187 + area80 = aeff80.GetBinContent(i+1,1)
  188 + r68 = angres.GetBinContent(i+1,1)
  189 + r80 = angres80.GetBinContent(i+1,1)
  190 + dE = eres.GetBinContent(i+1,1)
  191 + diffs = sens.GetBinContent(i+1,1)
  192 +
  193 + # Skip any NaNs
  194 + if math.isnan(area80):
  195 + continue
196 196
197 - # Compute energy (in MeV)  
198 - energy = math.pow(10.0, logE)*1.0e6  
199 - emin = math.pow(10.0, logE-0.1)*1.0e6  
200 - emax = math.pow(10.0, logE+0.1)*1.0e6  
201 - ewidth = emax-emin  
202 -  
203 - # Convert into background rate per steradian and MeV  
204 - bkg_rate = bgdsq / (0.01745329*0.01745329) / ewidth  
205 -  
206 - # Compute control background rate (this only works for KB files  
207 - # as they are for 80% containment radius). But we don't use this  
208 - # anyways, it's just for control on display ...  
209 - omega = 2.0 * math.pi * (1.0 - math.cos(math.radians(r80)))  
210 - if omega > 0.0:  
211 - bkg_rate2 = bgd / omega / ewidth  
212 - else:  
213 - bkg_rate2 = 0.0  
214 -  
215 - # Compute full effective area by dividing area80/0.80  
216 - area = area80/0.80 197 + # Compute energy (in MeV)
  198 + energy = math.pow(10.0, logE)*1.0e6
  199 + emin = math.pow(10.0, logE-0.1)*1.0e6
  200 + emax = math.pow(10.0, logE+0.1)*1.0e6
  201 + ewidth = emax-emin
  202 +
  203 + # Convert into background rate per steradian and MeV
  204 + bkg_rate = bgdsq / (0.01745329*0.01745329) / ewidth
  205 +
  206 + # Compute control background rate (this only works for KB files
  207 + # as they are for 80% containment radius). But we don't use this
  208 + # anyways, it's just for control on display ...
  209 + omega = 2.0 * math.pi * (1.0 - math.cos(math.radians(r80)))
  210 + if omega > 0.0:
  211 + bkg_rate2 = bgd / omega / ewidth
  212 + else:
  213 + bkg_rate2 = 0.0
  214 +
  215 + # Compute full effective area by dividing area80/0.80
  216 + area = area80/0.80
217 217
218 - # Write results in file  
219 - line = "%.1f %.1f %.4f %.4f %.4f %.5e %.5e" % \  
220 - (logE, area, r68, r80, dE, bgd, diffs)  
221 - fprf.write(line+"\n")  
222 - fbkg.write(str(energy)+" "+str(bkg_rate)+"\n") 218 + # Write results in file
  219 + line = "%.1f %.1f %.4f %.4f %.4f %.5e %.5e" % \
  220 + (logE, area, r68, r80, dE, bgd, diffs)
  221 + fprf.write(line+"\n")
  222 + fbkg.write(str(energy)+" "+str(bkg_rate)+"\n")
223 223
224 - # Show performance file  
225 - #print "%.1f %.1f %.1f %.4f %.4f %.4f %.7f %.5e %.5e %.5e (%.5e)" % \  
226 - # (logE, area, area80, r68, r80, dE, bgd, diffs, bgdsq, bkg_rate, bkg_rate2) 224 + # Show performance file
  225 + #print "%.1f %.1f %.1f %.4f %.4f %.4f %.7f %.5e %.5e %.5e (%.5e)" % \
  226 + # (logE, area, area80, r68, r80, dE, bgd, diffs, bgdsq, bkg_rate, bkg_rate2)
227 227
228 - # Write trailer  
229 - fprf.write("---------------------------------------------\n")  
230 - fprf.write("Notes\n")  
231 - fprf.write(" 1) log(E) = log10(E/TeV) - bin centre\n")  
232 - fprf.write(" 2) Eff Area - in square metres after background cut (no theta cut)\n")  
233 - fprf.write(" 3) Ang. Res - 68% containment radius of gamma-ray PSF post cuts - in degrees\n")  
234 - fprf.write(" 4) Ang. Res - 80% containment radius of gamma-ray PSF post cuts - in degrees\n")  
235 - fprf.write(" 5) Fractional Energy Resolution (rms)\n")  
236 - fprf.write(" 6) BG Rate - inside point-source selection region - post call cuts - in Hz\n")  
237 - fprf.write(" 7) Diff Sens - differential sensitivity for this bin expressed as E^2 dN/dE\n")  
238 - fprf.write(" - in erg cm^-2 s^-1 - for a 50 hours exposure - 5 sigma significance including\n")  
239 - fprf.write(" systematics and statistics and at least 10 photons.\n") 228 + # Write trailer
  229 + fprf.write("---------------------------------------------\n")
  230 + fprf.write("Notes\n")
  231 + fprf.write(" 1) log(E) = log10(E/TeV) - bin centre\n")
  232 + fprf.write(" 2) Eff Area - in square metres after background cut (no theta cut)\n")
  233 + fprf.write(" 3) Ang. Res - 68% containment radius of gamma-ray PSF post cuts - in degrees\n")
  234 + fprf.write(" 4) Ang. Res - 80% containment radius of gamma-ray PSF post cuts - in degrees\n")
  235 + fprf.write(" 5) Fractional Energy Resolution (rms)\n")
  236 + fprf.write(" 6) BG Rate - inside point-source selection region - post call cuts - in Hz\n")
  237 + fprf.write(" 7) Diff Sens - differential sensitivity for this bin expressed as E^2 dN/dE\n")
  238 + fprf.write(" - in erg cm^-2 s^-1 - for a 50 hours exposure - 5 sigma significance including\n")
  239 + fprf.write(" systematics and statistics and at least 10 photons.\n")
240 240
241 - # Close files  
242 - fprf.close()  
243 - fbkg.close() 241 + # Close files
  242 + fprf.close()
  243 + fbkg.close()
244 244
245 - # Return  
246 - return 245 + # Return
  246 + return
247 247
248 248
249 #==========================# 249 #==========================#
250 # Main routine entry point # 250 # Main routine entry point #
251 #==========================# 251 #==========================#
252 if __name__ == '__main__': 252 if __name__ == '__main__':
253 - """  
254 - """  
255 - # Get ROOT filename from command line argument  
256 - if (len(sys.argv) > 1):  
257 - filename = sys.argv[1]  
258 - else:  
259 - #caldb = "/Users/jurgen/Documents/Travail/Projects/CTA/WP-MC/root/IFAEPerformanceBCE_Nov2010"  
260 - #caldb = "/Users/jurgen/Documents/Travail/Projects/CTA/WP-MC/root/Kb_subarrays_root"  
261 - caldb = "/Users/jurgen/Documents/Travail/Projects/CTA/WP-MC/root/Kb_all_50deg_root" 253 + """
  254 + """
  255 + # Get ROOT filename from command line argument
  256 + if (len(sys.argv) > 1):
  257 + filename = sys.argv[1]
  258 + else:
  259 + #caldb = "/Users/jurgen/Documents/Travail/Projects/CTA/WP-MC/root/IFAEPerformanceBCE_Nov2010"
  260 + #caldb = "/Users/jurgen/Documents/Travail/Projects/CTA/WP-MC/root/Kb_subarrays_root"
  261 + caldb = "/Users/jurgen/Documents/Travail/Projects/CTA/WP-MC/root/Kb_all_50deg_root"
262 262
263 - # Get list of all performance files  
264 - files = glob.glob(caldb+"/*.root")  
265 - files.sort()  
266 -  
267 - # Loop over all files  
268 - for file in files:  
269 -  
270 - # Get filename  
271 - head, tail = os.path.split(file)  
272 -  
273 - # Build name  
274 - name = tail.strip(".root")  
275 - if name.find("kb_") != -1:  
276 - name = name.replace("_20deg", "")  
277 - elif name.find("IFAE_") != -1:  
278 - name = name.replace("Subarray", "")  
279 - name = name.replace("IFAE_", "")  
280 - name = "IFAE_"+name  
281 - i = name.find("hours")  
282 - if i != -1:  
283 - name = name[0:i+1]  
284 -  
285 - # Build output filenames  
286 - prfname = name + ".dat"  
287 - bkgname = "bkg_"+name+".txt"  
288 - #print prfname, bkgname 263 + # Get list of all performance files
  264 + files = glob.glob(caldb+"/*.root")
  265 + files.sort()
  266 +
  267 + # Loop over all files
  268 + for file in files:
  269 +
  270 + # Get filename
  271 + head, tail = os.path.split(file)
  272 +
  273 + # Build name
  274 + name = tail.strip(".root")
  275 + if name.find("kb_") != -1:
  276 + name = name.replace("_20deg", "")
  277 + elif name.find("IFAE_") != -1:
  278 + name = name.replace("Subarray", "")
  279 + name = name.replace("IFAE_", "")
  280 + name = "IFAE_"+name
  281 + i = name.find("hours")
  282 + if i != -1:
  283 + name = name[0:i+1]
  284 +
  285 + # Build output filenames
  286 + prfname = name + ".dat"
  287 + bkgname = "bkg_"+name+".txt"
  288 + #print prfname, bkgname
289 289
290 - # Create files  
291 - make_files(file, prfname, bkgname) 290 + # Create files
  291 + make_files(file, prfname, bkgname)
src/ctlike/ctlike.cpp
@@ -356,15 +356,18 @@ void ctlike::get_parameters(void) @@ -356,15 +356,18 @@ void ctlike::get_parameters(void)
356 // Get event file name 356 // Get event file name
357 std::string filename = (*this)["infile"].filename(); 357 std::string filename = (*this)["infile"].filename();
358 358
359 - // Try first to open as unbinned FITS file 359 + // Try first to open as FITS file
360 try { 360 try {
361 361
362 // Determine whether FITS file has EVENTS extension 362 // Determine whether FITS file has EVENTS extension
  363 + /*
363 GFits fits(filename); 364 GFits fits(filename);
364 bool is_unbinned = fits.contains("EVENTS"); 365 bool is_unbinned = fits.contains("EVENTS");
365 fits.close(); 366 fits.close();
  367 + */
366 368
367 // If FITS file has an events header then load as unbinned 369 // If FITS file has an events header then load as unbinned
  370 + /*
368 if (is_unbinned) { 371 if (is_unbinned) {
369 obs.load_unbinned(filename); 372 obs.load_unbinned(filename);
370 } 373 }
@@ -373,8 +376,12 @@ void ctlike::get_parameters(void) @@ -373,8 +376,12 @@ void ctlike::get_parameters(void)
373 else { 376 else {
374 obs.load_binned(filename); 377 obs.load_binned(filename);
375 } 378 }
  379 + */
376 380
377 - // Get task parameters for event file loading 381 + // Load data
  382 + obs.load(filename);
  383 +
  384 + // Get other task parameters
378 m_stat = gammalib::toupper((*this)["stat"].string()); 385 m_stat = gammalib::toupper((*this)["stat"].string());
379 m_caldb = (*this)["caldb"].string(); 386 m_caldb = (*this)["caldb"].string();
380 m_irf = (*this)["irf"].string(); 387 m_irf = (*this)["irf"].string();
@@ -408,7 +415,45 @@ void ctlike::get_parameters(void) @@ -408,7 +415,45 @@ void ctlike::get_parameters(void)
408 // Load observations from XML file 415 // Load observations from XML file
409 m_obs.load(filename); 416 m_obs.load(filename);
410 417
411 - } 418 + // Check if all observations have response information. If
  419 + // not, get the calibration database parameters and set
  420 + // the response properly
  421 + bool asked_for_response = false;
  422 + for (int i = 0; i < m_obs.size(); ++i) {
  423 +
  424 + // Get CTA observation
  425 + GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
  426 +
  427 + // Continue only if observation is a CTA observation
  428 + if (obs != NULL) {
  429 +
  430 + // If there is no effective area information then
  431 + // set response from task parameters
  432 + if (obs->response().aeff() == NULL) {
  433 +
  434 + // Get calibration parameters
  435 + if (!asked_for_response) {
  436 + m_caldb = (*this)["caldb"].string();
  437 + m_irf = (*this)["irf"].string();
  438 + asked_for_response = true;
  439 + }
  440 +
  441 + // Set response
  442 + GCaldb caldb;
  443 + if (gammalib::dir_exists(m_caldb)) {
  444 + caldb.rootdir(m_caldb);
  445 + }
  446 + else {
  447 + caldb.open("cta", m_caldb);
  448 + }
  449 + obs->response(m_irf, caldb);
  450 +
  451 + } // endif: there was no response
  452 + } // endif: observation was a CTA observation
  453 +
  454 + } // endfor: looped over observations
  455 +
  456 + } // endcatch: file was an XML file
412 457
413 } // endif: there was no observation in the container 458 } // endif: there was no observation in the container
414 459
src/ctlike/ctlike.par
@@ -17,14 +17,13 @@ @@ -17,14 +17,13 @@
17 # 17 #
18 # General parameters 18 # General parameters
19 #=================== 19 #===================
20 -infile, f, a, "events.fits",,,"Event list, counts map or observation definition file" 20 +infile, f, a, "events.fits",,, "Event list, counts map or observation definition file"
21 stat, s, h, "POISSON",,, "Optimization statistics" 21 stat, s, h, "POISSON",,, "Optimization statistics"
22 refit, b, h, no,,, "Do refitting?" 22 refit, b, h, no,,, "Do refitting?"
23 -edisp, b, h, no,,, "Apply energy dispersion?"  
24 -caldb, s, a, "$CTOOLS/share/caldb/data/cta/dummy",,, "Calibration database" 23 +caldb, s, a, "dummy",,, "Calibration database"
25 irf, s, a, "cta_dummy_irf",,, "Instrument response function" 24 irf, s, a, "cta_dummy_irf",,, "Instrument response function"
26 srcmdl, f, a, "$CTOOLS/share/models/crab.xml",,, "Source model" 25 srcmdl, f, a, "$CTOOLS/share/models/crab.xml",,, "Source model"
27 -outmdl, f, a, "crab_results.xml",,, "Source model output file" 26 +outmdl, f, a, "crab_results.xml",,, "Source model output file"
28 # 27 #
29 # Standard parameters 28 # Standard parameters
30 #==================== 29 #====================
src/ctobssim/ctobssim.par
@@ -18,12 +18,12 @@ @@ -18,12 +18,12 @@
18 # File information 18 # File information
19 #================= 19 #=================
20 infile, f, a, "$CTOOLS/share/models/crab.xml",,, "Model" 20 infile, f, a, "$CTOOLS/share/models/crab.xml",,, "Model"
21 -outfile,f, a, "events.fits",,, "Output event data file or observation definition file"  
22 -prefix, s, h, "sim_events_",,, "Prefix for event lists in observation definition file" 21 +outfile,f, a, "events.fits",,, "Output event data file or observation definition file"
  22 +prefix, s, h, "sim_events_",,, "Prefix for event lists in observation definition file"
23 # 23 #
24 # Observation parameters 24 # Observation parameters
25 #======================= 25 #=======================
26 -caldb, s, a, "$CTOOLS/share/caldb/data/cta/dummy",,, "Calibration database" 26 +caldb, s, a, "dummy",,, "Calibration database"
27 irf, s, a, "cta_dummy_irf",,, "Instrument response function" 27 irf, s, a, "cta_dummy_irf",,, "Instrument response function"
28 # 28 #
29 # Simulation parameters 29 # Simulation parameters
@@ -31,7 +31,7 @@ irf, s, a, &quot;cta_dummy_irf&quot;,,, &quot;Instrument response function&quot; @@ -31,7 +31,7 @@ irf, s, a, &quot;cta_dummy_irf&quot;,,, &quot;Instrument response function&quot;
31 seed, i, h, 1, , , "Random number generator seed" 31 seed, i, h, 1, , , "Random number generator seed"
32 ra, r, a, 83.63, 0,360, "RA of pointing (degrees)" 32 ra, r, a, 83.63, 0,360, "RA of pointing (degrees)"
33 dec, r, a, 22.01, -90, 90, "Dec of pointing (degrees)" 33 dec, r, a, 22.01, -90, 90, "Dec of pointing (degrees)"
34 -rad, r, a, 10.0, 0,180, "Radius of FOV (degrees)" 34 +rad, r, a, 5.0, 0,180, "Radius of FOV (degrees)"
35 tmin, r, a, 0.0, 0, , "Start time (MET in s)" 35 tmin, r, a, 0.0, 0, , "Start time (MET in s)"
36 tmax, r, a, 1800.0, 0, , "End time (MET in s)" 36 tmax, r, a, 1800.0, 0, , "End time (MET in s)"
37 emin, r, a, 0.1, 0, , "Lower energy limit (TeV)" 37 emin, r, a, 0.1, 0, , "Lower energy limit (TeV)"