Commit 59d3205fd82194ed570ced239171e9d7020b9e81

Authored by Jürgen Knödlseder
2 parents 997a7f46 c0a3fbd1

Merge branch '3510-test-rst' into integration

.gitignore
... ... @@ -99,6 +99,7 @@ test/butterfly.txt
99 99 test/.coverage
100 100 test/test_python_ctools.sh
101 101 test/test_python_cscripts.sh
  102 +test/tutorials
102 103 examples/*.fits
103 104 examples/*.xml
104 105 examples/*.dat
... ...
ChangeLog
1   -2020-12-03
  1 +2020-02-03
2 2  
3 3 * Version 2.0.0 released
4 4 ========================
5 5  
  6 + Add Sphinx tutorials test script (#3510)
6 7 Add "refit_if_failed" parameter to ctlike
7 8 Add models() method to ctobssim (#3441)
8 9 Add ctlike results to XML file (#3431)
... ...
Makefile.am
1 1 #############################################################################
2 2 # Makefile for ctools package #
3 3 # ------------------------------------------------------------------------- #
4   -# Copyright (C) 2016-2017 Juergen Knoedlseder #
  4 +# Copyright (C) 2016-2021 Juergen Knoedlseder #
5 5 # ------------------------------------------------------------------------- #
6 6 # #
7 7 # This program is free software: you can redistribute it and/or modify #
... ... @@ -211,13 +211,23 @@ valgrind:
211 211 # Add science verification rule
212 212 science-verification:
213 213 @if type test/science_verification.py >/dev/null 2>&1; then \
214   - cd test; \
215   - ./science_verification.py; \
216   - cd ..; \
  214 + cd test; \
  215 + ./science_verification.py; \
  216 + cd ..; \
217 217 else \
218 218 echo "science_verification.py not found in test."; \
219 219 fi
220 220  
  221 +# Add tutorials verification rule
  222 +check-tutorials:
  223 + @if type test/tutorials.py >/dev/null 2>&1; then \
  224 + cd test; \
  225 + ./tutorials.py; \
  226 + cd ..; \
  227 + else \
  228 + echo "tutorials.py not found in test."; \
  229 + fi
  230 +
221 231 # Add Sphinx rule
222 232 sphinx:
223 233 @if type sphinx-build >/dev/null 2>&1; then \
... ...
1 1 New Features and Important Changes in ctools 2.0.0
2 2  
3   -3 December 2020
  3 +3 February 2021
4 4  
5 5  
6 6 Introduction
... ... @@ -16,7 +16,8 @@ None
16 16  
17 17 Documentation
18 18 -------------
19   -None
  19 +Add test/tutorials.py script and check-tutorial target to test the code in the
  20 +Sphinx tutorials (#3510).
20 21  
21 22  
22 23 Python interface
... ...
README.md
1 1 ctools information
2 2 ==================
3   -* Version: 2.0.0.dev (3 December 2020)
  3 +* Version: 2.0.0.dev (3 February 2021)
4 4 * GammaLib dependency: 2.0.0.dev
5 5  
6 6 [![Build Status](https://cta-jenkins.irap.omp.eu/buildStatus/icon?job=ctools-integrate-os)](https://cta-jenkins.irap.omp.eu/job/ctools-integrate-os/)
... ...
cscripts/csspec.py
... ... @@ -2,7 +2,7 @@
2 2 # ==========================================================================
3 3 # Generates a spectrum.
4 4 #
5   -# Copyright (C) 2014-2020 Michael Mayer
  5 +# Copyright (C) 2014-2021 Michael Mayer
6 6 #
7 7 # This program is free software: you can redistribute it and/or modify
8 8 # it under the terms of the GNU General Public License as published by
... ... @@ -1071,7 +1071,7 @@ class csspec(ctools.csobservation):
1071 1071 energy_high = gammalib.GFitsTableDoubleCol('e_max', nrows)
1072 1072 norm = gammalib.GFitsTableDoubleCol('norm', nrows)
1073 1073 norm_err = gammalib.GFitsTableDoubleCol('norm_err', nrows)
1074   - ulim_values = gammalib.GFitsTableDoubleCol('norm_ul', nrows)
  1074 + norm_ul = gammalib.GFitsTableDoubleCol('norm_ul', nrows)
1075 1075 e2dnde = gammalib.GFitsTableDoubleCol('ref_e2dnde', nrows)
1076 1076 dnde = gammalib.GFitsTableDoubleCol('ref_dnde', nrows)
1077 1077 Npred_values = gammalib.GFitsTableDoubleCol('ref_npred', nrows)
... ... @@ -1084,7 +1084,7 @@ class csspec(ctools.csobservation):
1084 1084 energy_high.unit('TeV')
1085 1085 norm.unit('')
1086 1086 norm_err.unit('')
1087   - ulim_values.unit('')
  1087 + norm_ul.unit('')
1088 1088 e2dnde.unit('erg/cm2/s')
1089 1089 dnde.unit('counts/MeV/cm2/s')
1090 1090 Npred_values.unit('counts')
... ... @@ -1098,8 +1098,12 @@ class csspec(ctools.csobservation):
1098 1098 energy_low[i] = result['energy_low']
1099 1099 energy_high[i] = result['energy_high']
1100 1100 norm[i] = 1.0
1101   - norm_err[i] = result['flux_err'] / result['flux']
1102   - ulim_values[i] = result['ulimit'] / result['flux']
  1101 + if result['flux'] != 0.0:
  1102 + norm_err[i] = result['flux_err'] / result['flux']
  1103 + norm_ul[i] = result['ulimit'] / result['flux']
  1104 + else:
  1105 + norm_err[i] = 0.0
  1106 + norm_ul[i] = 0.0
1103 1107 e2dnde[i] = result['e2dnde']
1104 1108 dnde[i] = result['flux']
1105 1109 Npred_values[i] = result['Npred']
... ... @@ -1109,7 +1113,10 @@ class csspec(ctools.csobservation):
1109 1113 # Add likelihood scan values
1110 1114 for fbin in range(ncols):
1111 1115 dloglike_scan[i,fbin] = result['dloglike'][fbin]
1112   - norm_scan[i,fbin] = result['norm_scan'][fbin] / result['flux']
  1116 + if result['flux'] != 0.0:
  1117 + norm_scan[i,fbin] = result['norm_scan'][fbin] / result['flux']
  1118 + else:
  1119 + norm_scan[i,fbin] = 0.0
1113 1120  
1114 1121 # Initialise FITS Table with extension "SPECTRUM"
1115 1122 table = gammalib.GFitsBinTable(nrows)
... ... @@ -1125,7 +1132,7 @@ class csspec(ctools.csobservation):
1125 1132 table.append(energy_high)
1126 1133 table.append(norm)
1127 1134 table.append(norm_err)
1128   - table.append(ulim_values)
  1135 + table.append(norm_ul)
1129 1136 table.append(e2dnde)
1130 1137 table.append(dnde)
1131 1138 table.append(TSvalues)
... ...
cscripts/cstsmapsplit.py
... ... @@ -2,7 +2,7 @@
2 2 # ==========================================================================
3 3 # Create commands to split TS map computation
4 4 #
5   -# Copyright (C) 2016-2019 Michael Mayer
  5 +# Copyright (C) 2016-2021 Michael Mayer
6 6 #
7 7 # This program is free software: you can redistribute it and/or modify
8 8 # it under the terms of the GNU General Public License as published by
... ... @@ -247,6 +247,9 @@ class cstsmapsplit(ctools.cslikelihood):
247 247 for cmd in self._cmd:
248 248 f.write(cmd + '\n')
249 249  
  250 + # Add wait statement
  251 + f.write('wait\n')
  252 +
250 253 # Close file
251 254 f.close()
252 255  
... ...
doc/source/users/tutorials/1dc/CasA_model.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
  2 +<source_library title="source library">
  3 + <source name="Cassiopeia A" type="PointSource">
  4 + <spectrum type="PowerLaw">
  5 + <parameter name="Prefactor" value="1.45" scale="1e-18" min="0" free="1"/>
  6 + <parameter name="Index" value="2.75" scale="-1" min="-10" max="10" free="1"/>
  7 + <parameter name="PivotEnergy" value="1" scale="1e6" free="0"/>
  8 + </spectrum>
  9 + <spatialModel type="PointSource">
  10 + <parameter name="RA" value="350.8500" scale="1" free="0"/>
  11 + <parameter name="DEC" value="58.8150" scale="1" free="0"/>
  12 + </spatialModel>
  13 + </source>
  14 + <source name="Background model" type="CTAIrfBackground" instrument="CTA">
  15 + <spectrum type="PowerLaw">
  16 + <parameter name="Prefactor" value="1" scale="1" min="0.001" max="1000" free="1"/>
  17 + <parameter name="Index" value="0" scale="1" min="-5" max="5" free="1"/>
  18 + <parameter name="Scale" value="1" scale="1e6" min="0.01" max="1000" free="0"/>
  19 + </spectrum>
  20 + </source>
  21 +</source_library>
... ...
doc/source/users/tutorials/1dc/first_fitting.rst
... ... @@ -39,7 +39,7 @@ You run the :ref:`ctlike` tool as follows:
39 39 Input model definition XML file [$CTOOLS/share/models/crab.xml] bkgcube.xml
40 40 Output model definition XML file [crab_results.xml] results_stacked.xml
41 41  
42   -The tool will take a few minutes (on Mac OS X) to perform the model fitting,
  42 +The tool will take a few minutes (on Mac OS) to perform the model fitting,
43 43 and will write the results into an updated
44 44 :ref:`model definition file <glossary_moddef>`
45 45 containing the fitted model parameters and their statistical uncertainties.
... ... @@ -124,7 +124,7 @@ You create the butterfly diagram for ``Src001`` using
124 124 Stop value for last energy bin in TeV [100.0]
125 125 Output ASCII file [butterfly.txt] butterfly_src001.txt
126 126  
127   -and for `Src003`` using
  127 +and for ``Src003`` using
128 128  
129 129 .. code-block:: bash
130 130  
... ... @@ -192,28 +192,14 @@ a maximum likelihood model fit.
192 192  
193 193 Obviously, ``Src001`` has a spectral cut-off (red flux points) and hence is not
194 194 adequately described by a power law model. You should therefore replace the
195   -power law in the
196   -:ref:`model definition file <glossary_moddef>`
197   -by an exponentially cutoff power law, as shown below:
198   -
199   -.. code-block:: xml
200   -
201   - <?xml version="1.0" encoding="UTF-8" standalone="no"?>
202   - <source_library title="source library">
203   - <source name="Src001" type="PointSource">
204   - <spectrum type="ExponentialCutoffPowerLaw">
205   - <parameter name="Prefactor" scale="1e-18" value="5.7" min="1e-07" max="1000.0" free="1"/>
206   - <parameter name="Index" scale="-1" value="2.48" min="0.0" max="+5.0" free="1"/>
207   - <parameter name="CutoffEnergy" scale="1e7" value="1.0" min="0.01" max="1000.0" free="1"/>
208   - <parameter name="PivotEnergy" scale="1e6" value="0.3" min="0.01" max="1000.0" free="0"/>
209   - </spectrum>
210   - <spatialModel type="PointSource">
211   - <parameter name="RA" value="266.424004498437" scale="1" free="1" />
212   - <parameter name="DEC" value="-29.0049010253548" scale="1" free="1" />
213   - </spatialModel>
214   - </source>
215   - ...
216   - </source_library>
  195 +power law spectrum in the :ref:`model definition file <glossary_moddef>`
  196 +by an exponentially cutoff power law spectrum, as shown below (only the first
  197 +lines of the :ref:`model definition file <glossary_moddef>` are shown for
  198 +illustration):
  199 +
  200 +.. literalinclude:: results_stacked_eplaw.xml
  201 + :language: xml
  202 + :lines: 1-16
217 203  
218 204 Fitting this model to the data improves the fit and the resulting butterfly
219 205 diagram follows now reasonably well the spectral points:
... ...
doc/source/users/tutorials/1dc/first_onoff.rst
... ... @@ -208,6 +208,7 @@ follows:
208 208 Output observation definition XML file [onoff_obs.xml]
209 209 Output model definition XML file [onoff_model.xml]
210 210 Method for background estimation (REFLECTED|CUSTOM) [REFLECTED]
  211 + Shape of the source region (CIRCLE|RECT) [CIRCLE]
211 212 Coordinate system (CEL - celestial, GAL - galactic) (CEL|GAL) [CEL]
212 213 Right Ascension of source region centre (deg) (0-360) [83.63] 350.85
213 214 Declination of source region centre (deg) (-90-90) [22.01] 58.815
... ... @@ -355,29 +356,8 @@ The fit result can be inspected by peeking the log file:
355 356 provided as input model to :ref:`csphagen`. Here an example for an
356 357 input model:
357 358  
358   - .. code-block:: xml
359   -
360   - <?xml version="1.0" encoding="UTF-8" standalone="no"?>
361   - <source_library title="source library">
362   - <source name="Cassiopeia A" type="PointSource">
363   - <spectrum type="PowerLaw">
364   - <parameter name="Prefactor" value="1.45" scale="1e-18" min="0" free="1"/>
365   - <parameter name="Index" value="2.75" scale="-1" min="-10" max="10" free="1"/>
366   - <parameter name="PivotEnergy" value="1" scale="1e6" free="0"/>
367   - </spectrum>
368   - <spatialModel type="PointSource">
369   - <parameter name="RA" value="350.8500" scale="1" free="0"/>
370   - <parameter name="DEC" value="58.8150" scale="1" free="0"/>
371   - </spatialModel>
372   - </source>
373   - <source name="Background model" type="CTAIrfBackground" instrument="CTA">
374   - <spectrum type="PowerLaw">
375   - <parameter name="Prefactor" value="1" scale="1" min="0.001" max="1000" free="1"/>
376   - <parameter name="Index" value="0" scale="1" min="-5" max="5" free="1"/>
377   - <parameter name="Scale" value="1" scale="1e6" min="0.01" max="1000" free="0"/>
378   - </spectrum>
379   - </source>
380   - </source_library>
  359 + .. literalinclude:: CasA_model.xml
  360 + :language: xml
381 361  
382 362 Now rerun :ref:`csphagen` as follows:
383 363  
... ... @@ -395,6 +375,7 @@ The fit result can be inspected by peeking the log file:
395 375 Output observation definition XML file [onoff_obs.xml] onoff_obs_cstat.xml
396 376 Output model definition XML file [onoff_model.xml] onoff_model_cstat.xml
397 377 Method for background estimation (REFLECTED|CUSTOM) [REFLECTED]
  378 + Shape of the source region (CIRCLE|RECT) [CIRCLE]
398 379 Coordinate system (CEL - celestial, GAL - galactic) (CEL|GAL) [CEL]
399 380 Right Ascension of source region centre (deg) (0-360) [350.85]
400 381 Declination of source region centre (deg) (-90-90) [58.815]
... ... @@ -452,7 +433,7 @@ Like for a binned or unbinned analysis, you can create a butterfly diagram by ty
452 433  
453 434 $ ctbutterfly
454 435 Input event list, counts cube or observation definition XML file [events.fits] onoff_obs.xml
455   - Source of interest [Crab] Cassiopeia A
  436 + Source of interest [Crab] Dummy
456 437 Input model definition XML file [$CTOOLS/share/models/crab.xml] CasA_results.xml
457 438 Lower energy limit (TeV) [0.1]
458 439 Upper energy limit (TeV) [100.0] 50.0
... ... @@ -465,7 +446,7 @@ and the SED by typing
465 446 $ csspec
466 447 Input event list, counts cube, or observation definition XML file [events.fits] onoff_obs.xml
467 448 Input model definition XML file [$CTOOLS/share/models/crab.xml] CasA_results.xml
468   - Source name [Crab] Cassiopeia A
  449 + Source name [Crab] Dummy
469 450 Spectrum generation method (SLICE|NODES|AUTO) [AUTO]
470 451 Binning algorithm (FILE|LIN|LOG|POW) [LOG]
471 452 Lower energy limit (TeV) [0.1]
... ...
doc/source/users/tutorials/1dc/results_stacked_eplaw.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
  2 +<source_library title="source library">
  3 + <source name="Src001" type="PointSource">
  4 + <spectrum type="ExponentialCutoffPowerLaw">
  5 + <parameter name="Prefactor" scale="1e-18" value="5.7" min="1e-07" max="1000.0" free="1"/>
  6 + <parameter name="Index" scale="-1" value="2.48" min="0.0" max="+5.0" free="1"/>
  7 + <parameter name="CutoffEnergy" scale="1e7" value="1.0" min="0.01" max="1000.0" free="1"/>
  8 + <parameter name="PivotEnergy" scale="1e6" value="0.3" min="0.01" max="1000.0" free="0"/>
  9 + </spectrum>
  10 + <spatialModel type="PointSource">
  11 + <parameter name="RA" value="266.424004498437" scale="1" free="1"/>
  12 + <parameter name="DEC" value="-29.0049010253548" scale="1" free="1"/>
  13 + </spatialModel>
  14 + </source>
  15 + <source name="Src002" type="PointSource">
  16 + <spectrum type="PowerLaw">
  17 + <parameter name="Prefactor" value="1" scale="5.7e-18" min="0" free="1" />
  18 + <parameter name="Index" value="1" scale="-2.48" min="-4.03225806451613" max="4.03225806451613" free="1" />
  19 + <parameter name="PivotEnergy" value="1" scale="300000" free="0" />
  20 + </spectrum>
  21 + <spatialModel type="PointSource">
  22 + <parameter name="RA" value="264.802012181975" error="0" scale="1" free="1" />
  23 + <parameter name="DEC" value="-31.0050720432975" error="0" scale="1" free="1" />
  24 + </spatialModel>
  25 + </source>
  26 + <source name="Src003" type="PointSource">
  27 + <spectrum type="PowerLaw">
  28 + <parameter name="Prefactor" value="1" error="0" scale="5.7e-18" min="0" free="1" />
  29 + <parameter name="Index" value="1" error="-0" scale="-2.48" min="-4.03225806451613" max="4.03225806451613" free="1" />
  30 + <parameter name="PivotEnergy" value="1" scale="300000" free="0" />
  31 + </spectrum>
  32 + <spatialModel type="PointSource">
  33 + <parameter name="RA" value="266.831945177213" error="0" scale="1" free="1" />
  34 + <parameter name="DEC" value="-28.1460284439951" error="0" scale="1" free="1" />
  35 + </spatialModel>
  36 + </source>
  37 + <source name="Src004" type="PointSource">
  38 + <spectrum type="PowerLaw">
  39 + <parameter name="Prefactor" value="1" error="0" scale="5.7e-18" min="0" free="1" />
  40 + <parameter name="Index" value="1" error="-0" scale="-2.48" min="-4.03225806451613" max="4.03225806451613" free="1" />
  41 + <parameter name="PivotEnergy" value="1" scale="300000" free="0" />
  42 + </spectrum>
  43 + <spatialModel type="PointSource">
  44 + <parameter name="RA" value="268.244867737546" error="0" scale="1" free="1" />
  45 + <parameter name="DEC" value="-30.3728286753247" error="0" scale="1" free="1" />
  46 + </spatialModel>
  47 + </source>
  48 + <source name="Src005" type="PointSource">
  49 + <spectrum type="PowerLaw">
  50 + <parameter name="Prefactor" value="1" error="0" scale="5.7e-18" min="0" free="1" />
  51 + <parameter name="Index" value="1" error="-0" scale="-2.48" min="-4.03225806451613" max="4.03225806451613" free="1" />
  52 + <parameter name="PivotEnergy" value="1" scale="300000" free="0" />
  53 + </spectrum>
  54 + <spatialModel type="PointSource">
  55 + <parameter name="RA" value="270.424492131939" error="0" scale="1" free="1" />
  56 + <parameter name="DEC" value="-29.6624142779832" error="0" scale="1" free="1" />
  57 + </spatialModel>
  58 + </source>
  59 + <source name="Src006" type="PointSource">
  60 + <spectrum type="PowerLaw">
  61 + <parameter name="Prefactor" value="1" error="0" scale="5.7e-18" min="0" free="1" />
  62 + <parameter name="Index" value="1" error="-0" scale="-2.48" min="-4.03225806451613" max="4.03225806451613" free="1" />
  63 + <parameter name="PivotEnergy" value="1" scale="300000" free="0" />
  64 + </spectrum>
  65 + <spatialModel type="PointSource">
  66 + <parameter name="RA" value="263.809469183313" error="0" scale="1" free="1" />
  67 + <parameter name="DEC" value="-31.2230130593475" error="0" scale="1" free="1" />
  68 + </spatialModel>
  69 + </source>
  70 + <source name="BackgroundModel" type="CTACubeBackground" instrument="CTA,HESS,MAGIC,VERITAS">
  71 + <spectrum type="PowerLaw">
  72 + <parameter name="Prefactor" value="1" error="0" scale="1" min="0.01" max="100" free="1" />
  73 + <parameter name="Index" value="0" error="0" scale="1" min="-5" max="5" free="1" />
  74 + <parameter name="PivotEnergy" value="1" scale="1000000" free="0" />
  75 + </spectrum>
  76 + </source>
  77 +</source_library>
... ...
doc/source/users/tutorials/1dc/show_onoff_spectrum.py
... ... @@ -2,7 +2,7 @@
2 2 # ==========================================================================
3 3 # Display butterfly generated by ctbutterfly and spectrum generated by csspec
4 4 #
5   -# Copyright (C) 2018 Juergen Knoedlseder
  5 +# Copyright (C) 2018-2021 Juergen Knoedlseder
6 6 #
7 7 # This program is free software: you can redistribute it and/or modify
8 8 # it under the terms of the GNU General Public License as published by
... ... @@ -51,13 +51,14 @@ def plot_spectrum_butterfly(specfile, butterfile, fmt=&#39;o&#39;, color=&#39;red&#39;):
51 51 # Read spectrum file
52 52 fits = gammalib.GFits(specfile)
53 53 table = fits.table(1)
54   - c_energy = table['Energy']
55   - c_ed = table['ed_Energy']
56   - c_eu = table['eu_Energy']
57   - c_flux = table['Flux']
58   - c_eflux = table['e_Flux']
59   - c_ts = table['TS']
60   - c_upper = table['UpperLimit']
  54 + c_energy = table['e_ref']
  55 + c_ed = table['e_min']
  56 + c_eu = table['e_max']
  57 + c_flux = table['ref_e2dnde']
  58 + c_norm = table['norm']
  59 + c_eflux = table['norm_err']
  60 + c_upper = table['norm_ul']
  61 + c_ts = table['ts']
61 62  
62 63 # Read butterfly file
63 64 csv = gammalib.GCsv(butterfile)
... ... @@ -83,22 +84,23 @@ def plot_spectrum_butterfly(specfile, butterfile, fmt=&#39;o&#39;, color=&#39;red&#39;):
83 84  
84 85 # Get Test Statistic, flux and flux error
85 86 ts = c_ts.real(row)
86   - flx = c_flux.real(row)
87   - e_flx = c_eflux.real(row)
  87 + norm = c_norm.real(row)
  88 + flx = norm * c_flux.real(row)
  89 + e_flx = flx * c_eflux.real(row)
88 90  
89 91 # If Test Statistic is larger than 9 and flux error is smaller than
90 92 # flux then append flux plots ...
91 93 if ts > 9.0 and e_flx < flx:
92 94 energies.append(c_energy.real(row))
93   - flux.append(c_flux.real(row))
  95 + flux.append(flx)
94 96 ed_engs.append(c_ed.real(row))
95 97 eu_engs.append(c_eu.real(row))
96   - e_flux.append(c_eflux.real(row))
  98 + e_flux.append(e_flx)
97 99  
98 100 # ... otherwise append upper limit
99 101 else:
100 102 ul_energies.append(c_energy.real(row))
101   - ul_flux.append(c_upper.real(row))
  103 + ul_flux.append(flx*c_upper.real(row))
102 104 ul_ed_engs.append(c_ed.real(row))
103 105 ul_eu_engs.append(c_eu.real(row))
104 106  
... ... @@ -152,16 +154,18 @@ def show_spectrum_butterfly():
152 154 plt.grid()
153 155  
154 156 # Set usage string
155   - usage = 'show_onoff_spectrum.py [-t title] [specfile] [butterfile]'
  157 + usage = 'show_onoff_spectrum.py [-p plotfile] [-t title] [specfile] [butterfile]'
156 158  
157 159 # Set default options
158   - options = [{'option': '-t', 'value': 'Cassiopeia A'}]
  160 + options = [{'option': '-p', 'value': ''},
  161 + {'option': '-t', 'value': 'Cassiopeia A'}]
159 162  
160 163 # Get arguments and options from command line arguments
161 164 args, options = cscripts.ioutils.get_args_options(options, usage)
162 165  
163 166 # Extract script parameters from options
164   - title = options[0]['value']
  167 + plotfile = options[0]['value']
  168 + title = options[1]['value']
165 169  
166 170 # Set filenames
167 171 specfile = 'spectrum.fits'
... ... @@ -182,7 +186,10 @@ def show_spectrum_butterfly():
182 186 plt.title(title)
183 187  
184 188 # Show figure
185   - plt.show()
  189 + if len(plotfile) > 0:
  190 + plt.savefig(plotfile)
  191 + else:
  192 + plt.show()
186 193  
187 194 # Return
188 195 return
... ...
doc/source/users/tutorials/howto/extended_model.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
  2 +<source_library title="source library">
  3 + <source name="Src001" type="PointSource">
  4 + <spectrum type="ExponentialCutoffPowerLaw">
  5 + <parameter name="Prefactor" scale="1e-18" value="5.7" min="1e-07" max="1000.0" free="1"/>
  6 + <parameter name="Index" scale="-1" value="2.48" min="0.0" max="+5.0" free="1"/>
  7 + <parameter name="CutoffEnergy" scale="1e7" value="1.0" min="0.01" max="1000.0" free="1"/>
  8 + <parameter name="PivotEnergy" scale="1e6" value="0.3" min="0.01" max="1000.0" free="0"/>
  9 + </spectrum>
  10 + <spatialModel type="PointSource">
  11 + <parameter name="RA" scale="1" value="266.424" free="1" />
  12 + <parameter name="DEC" scale="1" value="-29.005" free="1" />
  13 + </spatialModel>
  14 + </source>
  15 + <source name="Src002" type="PointSource">
  16 + <spectrum type="PowerLaw">
  17 + <parameter name="Prefactor" scale="1e-18" value="5.7" min="1e-07" max="1000.0" free="1"/>
  18 + <parameter name="Index" scale="-1" value="2.48" min="0.0" max="5.0" free="1"/>
  19 + <parameter name="PivotEnergy" scale="1e6" value="0.3" min="0.01" max="1000.0" free="0"/>
  20 + </spectrum>
  21 + <spatialModel type="PointSource">
  22 + <parameter name="RA" scale="1" value="266.832" free="1" />
  23 + <parameter name="DEC" scale="1" value="-28.146" free="1" />
  24 + </spatialModel>
  25 + </source>
  26 + <source name="Src003" type="ExtendedSource" tscalc="1">
  27 + <spectrum type="PowerLaw">
  28 + <parameter name="Prefactor" scale="5.7e-18" value="1.0" min="0" max="1000.0" free="1"/>
  29 + <parameter name="Index" scale="-2.48" value="1.0" min="-4.0" max="4.0" free="1"/>
  30 + <parameter name="PivotEnergy" scale="300000" value="1.0" free="0" />
  31 + </spectrum>
  32 + <spatialModel type="RadialDisk">
  33 + <parameter name="RA" scale="1.0" value="266.3070" min="-360" max="360" free="1"/>
  34 + <parameter name="DEC" scale="1.0" value="-30.1876" min="-90" max="90" free="1"/>
  35 + <parameter name="Radius" scale="1.0" value="0.3" min="0.001" max="10" free="1"/>
  36 + </spatialModel>
  37 + </source>
  38 + <source name="IEM" type="DiffuseSource">
  39 + <spectrum type="ConstantValue">
  40 + <parameter name="Value" scale="1" value="1" min="1e-05" max="100000" free="1"/>
  41 + </spectrum>
  42 + <spatialModel type="MapCubeFunction" file="$CTADATA/models/cube_iem.fits">
  43 + <parameter name="Normalization" scale="1" value="1" min="0.001" max="1000" free="0"/>
  44 + </spatialModel>
  45 + </source>
  46 + <source name="Background" type="CTACubeBackground" instrument="CTA">
  47 + <spectrum type="PowerLaw">
  48 + <parameter name="Prefactor" scale="1" value="1" min="0.01" max="100" free="1"/>
  49 + <parameter name="Index" scale="1" value="0" min="-5" max="5" free="1"/>
  50 + <parameter name="PivotEnergy" scale="1000000" value="1" min="0.01" max="1000.0" free="0"/>
  51 + </spectrum>
  52 + </source>
  53 +</source_library>
... ...
doc/source/users/tutorials/howto/howto_connect_irfs.rst
... ... @@ -52,21 +52,11 @@ array:
52 52 Output event data file or observation definition XML file [north.fits] south.fits
53 53  
54 54 You now need to combine the information about these two observations in an
55   -observation definition XML file:
  55 +observation definition XML file. Let's put the following content in in the
  56 +``obs_irf.xml``:
56 57  
57   -.. code-block:: xml
58   -
59   - <?xml version="1.0" standalone="no"?>
60   - <observation_list title="observation library">
61   - <observation name="Crab" id="00001" instrument="CTA">
62   - <parameter name="EventList" file="north.fits"/>
63   - <parameter name="Calibration" database="prod2" response="North_0.5h"/>
64   - </observation>
65   - <observation name="Crab" id="00002" instrument="CTA">
66   - <parameter name="EventList" file="south.fits"/>
67   - <parameter name="Calibration" database="prod2" response="South_0.5h"/>
68   - </observation>
69   - </observation_list>
  58 +.. literalinclude:: obs_irf.xml
  59 + :language: xml
70 60  
71 61 Each observation now has a ``Calibration`` parameter with the attributes
72 62 ``database`` and ``response``.
... ...
doc/source/users/tutorials/howto/howto_extent.rst
... ... @@ -33,22 +33,12 @@ To do this, copy the 1DC :ref:`model definition &lt;glossary_moddef&gt;` file
33 33  
34 34 $ cp $CTOOLS/share/models/1dc_howto.xml extended_model.xml
35 35  
36   -and add the following source to it
37   -
38   -.. code-block:: xml
39   -
40   - <source name="Src003" type="ExtendedSource" tscalc="1">
41   - <spectrum type="PowerLaw">
42   - <parameter name="Prefactor" scale="5.7e-18" value="1.0" min="0" max="1000.0" free="1"/>
43   - <parameter name="Index" scale="-2.48" value="1.0" min="-4.0" max="4.0" free="1"/>
44   - <parameter name="PivotEnergy" scale="300000" value="1.0" free="0" />
45   - </spectrum>
46   - <spatialModel type="RadialDisk">
47   - <parameter name="RA" scale="1.0" value="266.3070" min="-360" max="360" free="1"/>
48   - <parameter name="DEC" scale="1.0" value="-30.1876" min="-90" max="90" free="1"/>
49   - <parameter name="Radius" scale="1.0" value="0.3" min="0.001" max="10" free="1"/>
50   - </spatialModel>
51   - </source>
  36 +to :download:`extended_model.xml <extended_model.xml>` and add the following
  37 +source to it:
  38 +
  39 +.. literalinclude:: extended_model.xml
  40 + :language: xml
  41 + :lines: 26-37
52 42  
53 43 Then run :ref:`ctlike` to fit the parameters of ``Src003`` together with the
54 44 other source parameters
... ...
doc/source/users/tutorials/howto/howto_ts.rst
... ... @@ -19,28 +19,13 @@ Copy the 1DC :ref:`model definition &lt;glossary_moddef&gt;` file
19 19 $ cp $CTOOLS/share/models/1dc_howto.xml ts_model.xml
20 20  
21 21 and modify the file by adding ``tscalc="1"`` to the ``<source>`` tag for
22   -source ``Src1``. Your
  22 +source ``Src1``. The first lines of the :download:`ts_model.xml <ts_model.xml>`
23 23 :ref:`model definition XML file <glossary_moddef>`
24 24 should then look like this:
25 25  
26   -.. code-block:: xml
27   -
28   - <?xml version="1.0" encoding="UTF-8" standalone="no"?>
29   - <source_library title="source library">
30   - <source name="Src001" type="PointSource" tscalc="1">
31   - <spectrum type="ExponentialCutoffPowerLaw">
32   - <parameter name="Prefactor" scale="1e-18" value="5.7" min="1e-07" max="1000.0" free="1"/>
33   - <parameter name="Index" scale="-1" value="2.48" min="0.0" max="+5.0" free="1"/>
34   - <parameter name="CutoffEnergy" scale="1e7" value="1.0" min="0.01" max="1000.0" free="1"/>
35   - <parameter name="PivotEnergy" scale="1e6" value="0.3" min="0.01" max="1000.0" free="0"/>
36   - </spectrum>
37   - <spatialModel type="PointSource">
38   - <parameter name="RA" scale="1" value="266.424" free="1" />
39   - <parameter name="DEC" scale="1" value="-29.005" free="1" />
40   - </spatialModel>
41   - </source>
42   - ...
43   - </source_library>
  26 +.. literalinclude:: ts_model.xml
  27 + :language: xml
  28 + :lines: 1-14
44 29  
45 30 Then run :ref:`ctlike` to compute the so-called Test Statistic, which is twice
46 31 the difference between the log-likelihood value obtained for a fit that includes
... ...
doc/source/users/tutorials/howto/howto_tsmap.rst
... ... @@ -100,12 +100,13 @@ The figure below shows the resulting Test Statistic map.
100 100 cttsmap inobs=cntcube.fits inmodel=$CTOOLS/share/models/test_cube.xml srcname=Test expcube=expcube.fits psfcube=psfcube.fits bkgcube=bkgcube.fits nxpix=10 nypix=10 binsz=0.05 coordsys=GAL proj=CAR xref=0.0 yref=0.0 logL0=-447785.5939102234 binmin=70 binmax=80 outmap=tsmap_split_7.fits logfile=tsmap_split_7.log &
101 101 cttsmap inobs=cntcube.fits inmodel=$CTOOLS/share/models/test_cube.xml srcname=Test expcube=expcube.fits psfcube=psfcube.fits bkgcube=bkgcube.fits nxpix=10 nypix=10 binsz=0.05 coordsys=GAL proj=CAR xref=0.0 yref=0.0 logL0=-447785.5939102234 binmin=80 binmax=90 outmap=tsmap_split_8.fits logfile=tsmap_split_8.log &
102 102 cttsmap inobs=cntcube.fits inmodel=$CTOOLS/share/models/test_cube.xml srcname=Test expcube=expcube.fits psfcube=psfcube.fits bkgcube=bkgcube.fits nxpix=10 nypix=10 binsz=0.05 coordsys=GAL proj=CAR xref=0.0 yref=0.0 logL0=-447785.5939102234 binmin=90 binmax=100 outmap=tsmap_split_9.fits logfile=tsmap_split_9.log &
  103 + wait
103 104  
104 105 Now run the ASCII file by typing
105 106  
106 107 .. code-block:: bash
107 108  
108   - ./tsmap_commands.dat
  109 + $ ./tsmap_commands.dat
109 110  
110 111 This will execute 10 :ref:`cttsmap` jobs in parallel. Each of the jobs will
111 112 compute 10 pixels of the Test Statistic map. You combine the resulting 10
... ...
doc/source/users/tutorials/howto/howto_ulimit.rst
... ... @@ -22,21 +22,12 @@ To do this, copy the 1DC :ref:`model definition &lt;glossary_moddef&gt;` file
22 22  
23 23 $ cp $CTOOLS/share/models/1dc_howto.xml ulimit_model.xml
24 24  
25   -and add the following source to it
26   -
27   -.. code-block:: xml
28   -
29   - <source name="Test" type="PointSource">
30   - <spectrum type="PowerLaw">
31   - <parameter name="Prefactor" value="1" scale="1.0e-20" min="0" free="1"/>
32   - <parameter name="Index" value="1" scale="-2.48" min="-5" max="5" free="0"/>
33   - <parameter name="PivotEnergy" value="1" scale="300000" free="0"/>
34   - </spectrum>
35   - <spatialModel type="PointSource">
36   - <parameter name="GLON" value="0.1" scale="1" free="0"/>
37   - <parameter name="GLAT" value="0.4" scale="1" free="0"/>
38   - </spatialModel>
39   - </source>
  25 +to :download:`ulimit_model.xml <ulimit_model.xml>` and add the following
  26 +source to it:
  27 +
  28 +.. literalinclude:: ulimit_model.xml
  29 + :language: xml
  30 + :lines: 3-13
40 31  
41 32 Now run the :ref:`ctulimit` tool as follows:
42 33  
... ...
doc/source/users/tutorials/howto/obs_irf.xml 0 → 100644
  1 +<?xml version="1.0" standalone="no"?>
  2 +<observation_list title="observation library">
  3 + <observation name="Crab" id="00001" instrument="CTA">
  4 + <parameter name="EventList" file="north.fits"/>
  5 + <parameter name="Calibration" database="prod2" response="North_0.5h"/>
  6 + </observation>
  7 + <observation name="Crab" id="00002" instrument="CTA">
  8 + <parameter name="EventList" file="south.fits"/>
  9 + <parameter name="Calibration" database="prod2" response="South_0.5h"/>
  10 + </observation>
  11 +</observation_list>
  12 +
... ...
doc/source/users/tutorials/howto/ts_model.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
  2 +<source_library title="source library">
  3 + <source name="Src001" type="PointSource" tscalc="1">
  4 + <spectrum type="ExponentialCutoffPowerLaw">
  5 + <parameter name="Prefactor" scale="1e-18" value="5.7" min="1e-07" max="1000.0" free="1"/>
  6 + <parameter name="Index" scale="-1" value="2.48" min="0.0" max="+5.0" free="1"/>
  7 + <parameter name="CutoffEnergy" scale="1e7" value="1.0" min="0.01" max="1000.0" free="1"/>
  8 + <parameter name="PivotEnergy" scale="1e6" value="0.3" min="0.01" max="1000.0" free="0"/>
  9 + </spectrum>
  10 + <spatialModel type="PointSource">
  11 + <parameter name="RA" scale="1" value="266.424" free="1" />
  12 + <parameter name="DEC" scale="1" value="-29.005" free="1" />
  13 + </spatialModel>
  14 + </source>
  15 + <source name="Src002" type="PointSource">
  16 + <spectrum type="PowerLaw">
  17 + <parameter name="Prefactor" scale="1e-18" value="5.7" min="1e-07" max="1000.0" free="1"/>
  18 + <parameter name="Index" scale="-1" value="2.48" min="0.0" max="5.0" free="1"/>
  19 + <parameter name="PivotEnergy" scale="1e6" value="0.3" min="0.01" max="1000.0" free="0"/>
  20 + </spectrum>
  21 + <spatialModel type="PointSource">
  22 + <parameter name="RA" scale="1" value="266.832" free="1" />
  23 + <parameter name="DEC" scale="1" value="-28.146" free="1" />
  24 + </spatialModel>
  25 + </source>
  26 + <source name="IEM" type="DiffuseSource">
  27 + <spectrum type="ConstantValue">
  28 + <parameter name="Value" scale="1" value="1" min="1e-05" max="100000" free="1"/>
  29 + </spectrum>
  30 + <spatialModel type="MapCubeFunction" file="$CTADATA/models/cube_iem.fits">
  31 + <parameter name="Normalization" scale="1" value="1" min="0.001" max="1000" free="0"/>
  32 + </spatialModel>
  33 + </source>
  34 + <source name="Background" type="CTACubeBackground" instrument="CTA">
  35 + <spectrum type="PowerLaw">
  36 + <parameter name="Prefactor" scale="1" value="1" min="0.01" max="100" free="1"/>
  37 + <parameter name="Index" scale="1" value="0" min="-5" max="5" free="1"/>
  38 + <parameter name="PivotEnergy" scale="1000000" value="1" min="0.01" max="1000.0" free="0"/>
  39 + </spectrum>
  40 + </source>
  41 +</source_library>
... ...
doc/source/users/tutorials/howto/ulimit_model.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
  2 +<source_library title="source library">
  3 + <source name="Test" type="PointSource">
  4 + <spectrum type="PowerLaw">
  5 + <parameter name="Prefactor" value="1" scale="1.0e-20" min="0" free="1"/>
  6 + <parameter name="Index" value="1" scale="-2.48" min="-5" max="5" free="0"/>
  7 + <parameter name="PivotEnergy" value="1" scale="300000" free="0"/>
  8 + </spectrum>
  9 + <spatialModel type="PointSource">
  10 + <parameter name="GLON" value="0.1" scale="1" free="0"/>
  11 + <parameter name="GLAT" value="0.4" scale="1" free="0"/>
  12 + </spatialModel>
  13 + </source>
  14 + <source name="Src001" type="PointSource">
  15 + <spectrum type="ExponentialCutoffPowerLaw">
  16 + <parameter name="Prefactor" scale="1e-18" value="5.7" min="1e-07" max="1000.0" free="1"/>
  17 + <parameter name="Index" scale="-1" value="2.48" min="0.0" max="+5.0" free="1"/>
  18 + <parameter name="CutoffEnergy" scale="1e7" value="1.0" min="0.01" max="1000.0" free="1"/>
  19 + <parameter name="PivotEnergy" scale="1e6" value="0.3" min="0.01" max="1000.0" free="0"/>
  20 + </spectrum>
  21 + <spatialModel type="PointSource">
  22 + <parameter name="RA" scale="1" value="266.424" free="1" />
  23 + <parameter name="DEC" scale="1" value="-29.005" free="1" />
  24 + </spatialModel>
  25 + </source>
  26 + <source name="Src002" type="PointSource">
  27 + <spectrum type="PowerLaw">
  28 + <parameter name="Prefactor" scale="1e-18" value="5.7" min="1e-07" max="1000.0" free="1"/>
  29 + <parameter name="Index" scale="-1" value="2.48" min="0.0" max="5.0" free="1"/>
  30 + <parameter name="PivotEnergy" scale="1e6" value="0.3" min="0.01" max="1000.0" free="0"/>
  31 + </spectrum>
  32 + <spatialModel type="PointSource">
  33 + <parameter name="RA" scale="1" value="266.832" free="1" />
  34 + <parameter name="DEC" scale="1" value="-28.146" free="1" />
  35 + </spatialModel>
  36 + </source>
  37 + <source name="IEM" type="DiffuseSource">
  38 + <spectrum type="ConstantValue">
  39 + <parameter name="Value" scale="1" value="1" min="1e-05" max="100000" free="1"/>
  40 + </spectrum>
  41 + <spatialModel type="MapCubeFunction" file="$CTADATA/models/cube_iem.fits">
  42 + <parameter name="Normalization" scale="1" value="1" min="0.001" max="1000" free="0"/>
  43 + </spatialModel>
  44 + </source>
  45 + <source name="Background" type="CTACubeBackground" instrument="CTA">
  46 + <spectrum type="PowerLaw">
  47 + <parameter name="Prefactor" scale="1" value="1" min="0.01" max="100" free="1"/>
  48 + <parameter name="Index" scale="1" value="0" min="-5" max="5" free="1"/>
  49 + <parameter name="PivotEnergy" scale="1000000" value="1" min="0.01" max="1000.0" free="0"/>
  50 + </spectrum>
  51 + </source>
  52 +</source_library>
... ...
test/tutorials.py 0 → 100755
  1 +#! /usr/bin/env python
  2 +# ==========================================================================
  3 +# This script tests the Sphinx tutorials
  4 +#
  5 +# Usage:
  6 +# ./tutorials.py
  7 +#
  8 +# --------------------------------------------------------------------------
  9 +#
  10 +# Copyright (C) 2021 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 +import os
  27 +import sys
  28 +import stat
  29 +import subprocess
  30 +import gammalib
  31 +import ctools
  32 +import cscripts
  33 +
  34 +
  35 +# ===================================== #
  36 +# Test class for tutorials verification #
  37 +# ===================================== #
  38 +class tutorials(gammalib.GPythonTestSuite):
  39 + """
  40 + Test class for tutorials verification
  41 + """
  42 + # Constructor
  43 + def __init__(self):
  44 + """
  45 + Constructor
  46 + """
  47 + # Call base class constructor
  48 + gammalib.GPythonTestSuite.__init__(self)
  49 +
  50 + # Initialise private members
  51 + self._rst_step = 0 # Step counter
  52 + self._rst_path = '' # Path where the tested RST file resides
  53 + self._cwd = '.' # Current working directory
  54 +
  55 + # Return
  56 + return
  57 +
  58 + # Clean pfiles directory
  59 + def _clean_pfiles(self):
  60 + """
  61 + Clean pfiles directory
  62 + """
  63 + # Remove all files from pfiles directory
  64 + os.system('rm -rf %s/*.par' % os.environ['PFILES'])
  65 +
  66 + # Return
  67 + return
  68 +
  69 + # Set result directory
  70 + def _set_result_dir(self, path):
  71 + """
  72 + Set result directory
  73 +
  74 + Parameters
  75 + ----------
  76 + path : str
  77 + Result directory path
  78 + """
  79 + # Set default working directory
  80 + os.chdir(self._cwd)
  81 +
  82 + # Create result directory
  83 + try:
  84 + os.makedirs(path)
  85 + except:
  86 + pass
  87 +
  88 + # Clear result directory
  89 + os.system('rm -rf %s/*' % path)
  90 +
  91 + # Step in result directory
  92 + os.chdir(path)
  93 +
  94 + # Return
  95 + return
  96 +
  97 + # Test command execution
  98 + def _test_execute_cmd(self, cmd, args):
  99 + """
  100 + Test command execution
  101 +
  102 + Tests the execution of a command. Currently supported commands are:
  103 + - cd
  104 + - mkdir
  105 + - nano
  106 + - ./xxxxxx (any command xxxxxx that resides in the directory of the RST file)
  107 +
  108 + Parameters
  109 + ----------
  110 + cmd : str
  111 + Command line
  112 + args : list of str
  113 + Additional lines following the command line
  114 + """
  115 + # Split command line
  116 + cmdline = cmd.split(' ')
  117 +
  118 + # Check command line
  119 + self.test_assert((len(cmdline) > 0), 'Test that command line is not empty.', cmd)
  120 +
  121 + # Continue only if valid
  122 + if len(cmdline) > 0:
  123 +
  124 + # Extract command
  125 + command = gammalib.strip_whitespace(cmdline[0])
  126 +
  127 + # Handle "mkdir" command
  128 + if command == 'mkdir':
  129 + self.test_value(len(cmdline), 2, 'Test number of arguments for "mkdir".')
  130 + self.test_value(len(args), 0, 'Test that no lines follow "mkdir".')
  131 + if len(cmdline) > 1:
  132 + dirname = gammalib.strip_whitespace(cmdline[1])
  133 + os.system('mkdir -p %s' % dirname)
  134 +
  135 + # Handle "cd" command
  136 + elif command == 'cd':
  137 + self.test_value(len(cmdline), 2, 'Test number of arguments for "cd".')
  138 + self.test_value(len(args), 0, 'Test that no lines follow "cd".')
  139 + if len(cmdline) > 1:
  140 + dirname = gammalib.strip_whitespace(cmdline[1])
  141 + os.chdir(dirname)
  142 +
  143 + # Handle "cp" command
  144 + elif command == 'cp':
  145 + self.test_value(len(cmdline), 3, 'Test number of arguments for "cp".')
  146 + self.test_value(len(args), 0, 'Test that no lines follow "cp".')
  147 + if len(cmdline) > 2:
  148 + src = gammalib.expand_env(gammalib.strip_whitespace(cmdline[1]))
  149 + dst = gammalib.expand_env(gammalib.strip_whitespace(cmdline[2]))
  150 + os.system('cp %s %s' % (src, dst))
  151 +
  152 + # Handle "nano" command
  153 + elif command == 'nano':
  154 + self.test_value(len(cmdline), 2, 'Test number of arguments for "nano".')
  155 + self.test_assert((len(args) > 0), 'Test that lines follow "nano".')
  156 + if len(cmdline) > 1:
  157 + fname = gammalib.strip_whitespace(cmdline[1])
  158 + f = open(fname, 'wb')
  159 + for arg in args:
  160 + f.write(arg)
  161 + f.close()
  162 +
  163 + # Handle script execution
  164 + elif command[0:2] == './':
  165 +
  166 + # Set script name. If the script does not exist in result
  167 + # directory then assume it exists in Sphinx rst directory
  168 + if os.path.isfile(command[2:]):
  169 + script = './%s' % (command[2:])
  170 + else:
  171 + script = '%s/%s' % (self._rst_path, command[2:])
  172 +
  173 + # Make sure that script is executable
  174 + mode = os.stat(script).st_mode
  175 + mode |= (mode & 0o444) >> 2
  176 + os.chmod(script, mode)
  177 +
  178 + # Build command line
  179 + plotname, _ = os.path.splitext(command[2:])
  180 + cmd = '%s -p %s.png' % (script, plotname)
  181 + self.test_assert((len(args) == 0), 'Test script "%s".' % command[2:])
  182 +
  183 + # Execute command, make sure we catch any exception
  184 + try:
  185 + rc = os.system(cmd+' > tutorials_test_output.dmp 2>&1')
  186 + except:
  187 + rc = -1
  188 +
  189 + # If an exception occured then put output on screen
  190 + msg = ''
  191 + if rc != 0:
  192 + f = open('tutorials_test_output.dmp', 'r')
  193 + for line in f:
  194 + msg += line
  195 + f.close()
  196 +
  197 + # Remove test output
  198 + os.remove('tutorials_test_output.dmp')
  199 +
  200 + # Set error flag and text
  201 + name = 'Test execution of "%s".' % command[2:]
  202 + self.test_value(rc, 0, name, msg)
  203 +
  204 + # Handle plotting scripts
  205 + elif command[0:7] == '$CTOOLS':
  206 +
  207 + # Build command line
  208 + script = gammalib.expand_env(cmdline[0])
  209 + filename = cmdline[1]
  210 + plotname, _ = os.path.splitext(filename)
  211 + cmd = '%s -p %s.png %s' % (script, plotname, filename)
  212 + self.test_assert((len(args) == 0), 'Test script "%s".' % command)
  213 +
  214 + # Execute command, make sure we catch any exception
  215 + try:
  216 + rc = os.system(cmd+' > tutorials_test_output.dmp 2>&1')
  217 + except:
  218 + rc = -1
  219 +
  220 + # If an exception occured then put output on screen
  221 + msg = ''
  222 + if rc != 0:
  223 + f = open('tutorials_test_output.dmp', 'r')
  224 + for line in f:
  225 + msg += line
  226 + f.close()
  227 +
  228 + # Remove test output
  229 + os.remove('tutorials_test_output.dmp')
  230 +
  231 + # Set error flag and text
  232 + name = 'Test execution of "%s".' % script
  233 + self.test_value(rc, 0, name, msg)
  234 +
  235 + # Handle unsupported Xspec commands
  236 + elif command[0:5] == 'xspec':
  237 + pass
  238 +
  239 + # Signal that the command was not known
  240 + else:
  241 + name = 'Test command "%s".' % command
  242 + msg = 'Unknown command "%s"' % command
  243 + self.test_assert(False, name, msg)
  244 +
  245 + # Return
  246 + return
  247 +
  248 + # Test line execution
  249 + def _test_line_execution(self, lines):
  250 + """
  251 + Test the execution of a sequence of commands
  252 +
  253 + Parameters
  254 + ----------
  255 + lines : list of str
  256 + Command lines
  257 + """
  258 + # Initialise commands and arguments
  259 + cmd = ''
  260 + args = ''
  261 +
  262 + # Loop over all lines
  263 + for line in lines:
  264 +
  265 + # If line starts with a string then extract or execute command
  266 + if line[0:2] == '$ ':
  267 +
  268 + # If we have a command then execute it
  269 + if cmd != '':
  270 + self._test_execute_cmd(cmd, args)
  271 +
  272 + # Set new command
  273 + cmd = line[2:]
  274 + args = ''
  275 +
  276 + # ... otherwise add argument
  277 + else:
  278 + args += '%s\n' % (gammalib.strip_whitespace(line))
  279 +
  280 + # If there is a pending command then execute it
  281 + if cmd != '':
  282 + self._test_execute_cmd(cmd, args)
  283 +
  284 + # Return
  285 + return
  286 +
  287 + # Test ctool or cscript execution
  288 + def _test_ctool_execution(self, tool, lines):
  289 + """
  290 + Test the execution of a ctool or cscript
  291 +
  292 + The methods test the execution of a ctool or cscript as executed from
  293 + the command line emulating the entering of the queried parameters. Any
  294 + error that occurs in the execution will be signaled and the Python
  295 + traceback will be put in the error message field.
  296 +
  297 + Parameters
  298 + ----------
  299 + tool : str
  300 + ctool name
  301 + lines : list of str
  302 + Command lines
  303 + """
  304 + # Increment step counter
  305 + self._rst_step += 1
  306 +
  307 + # Initialise arguments
  308 + args = ''
  309 +
  310 + # Set command
  311 + cmd = tool.split(' ')
  312 +
  313 + # Append lines
  314 + for line in lines:
  315 + pos = line.find(']')
  316 + if pos != -1:
  317 + arg = gammalib.strip_whitespace(line[pos+1:])
  318 + args += '%s\n' % arg
  319 +
  320 + # Create subprocess for tool
  321 + p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
  322 + stdin=subprocess.PIPE,
  323 + stderr=subprocess.PIPE)
  324 +
  325 + # Write command line parameters
  326 + p.stdin.write(b'%s' % args)
  327 +
  328 + # Get results
  329 + res = p.communicate()
  330 +
  331 + # Close pipeline
  332 + p.stdin.close()
  333 +
  334 + # Set error flag and message
  335 + if '*** ERROR' in res[0]:
  336 + success = False
  337 + msg = res[0]
  338 + elif len(res[1]) > 0:
  339 + success = False
  340 + msg = res[1]
  341 + else:
  342 + success = True
  343 + msg = ''
  344 + name = 'Test execution of "%s".' % tool
  345 + self.test_assert(success, name, msg)
  346 +
  347 + # Preprend step counter to logfile
  348 + logfile = gammalib.GFilename('%s.log' % (cmd[0]))
  349 + if logfile.exists():
  350 + src = logfile.url()
  351 + dst = '%2.2d_%s' % (self._rst_step, src)
  352 + os.system('mv %s %s' % (src, dst))
  353 +
  354 + # Return
  355 + return
  356 +
  357 + # Test workflow step
  358 + def _test_step(self, tool, lines):
  359 + """
  360 + Test workflow step
  361 +
  362 + Parameters
  363 + ----------
  364 + tool : str
  365 + Tool name
  366 + lines : list of str
  367 + Command lines
  368 + """
  369 + # If tool is a ctool or cscript then test ctool or cscript execution
  370 + if tool[0:2] == 'ct' or tool[0:2] == 'cs':
  371 + self._test_ctool_execution(tool, lines)
  372 +
  373 + # ... otherwise test line execution
  374 + else:
  375 + all_lines = ['$ %s' % tool]
  376 + all_lines.extend(lines)
  377 + self._test_line_execution(all_lines)
  378 +
  379 + # Return
  380 + return
  381 +
  382 + # Copy file
  383 + def _copy_file(self, filename):
  384 + """
  385 + Copy file from rst directory into working directory
  386 + """
  387 + # Set source filename
  388 + src = self._rst_path + '/' + filename
  389 +
  390 + # Copy file
  391 + os.system('cp %s %s' % (src, filename))
  392 +
  393 + # Return
  394 + return
  395 +
  396 + # Test Sphinx rst file
  397 + def _test_rst_file(self, filename):
  398 + """
  399 + Test Sphinx rst file
  400 +
  401 + Parameters
  402 + ----------
  403 + filename : str
  404 + Sphinx rst filename
  405 + """
  406 + # Set RST path
  407 + self._rst_path = os.path.abspath(os.path.dirname(filename))
  408 +
  409 + # Open RST file
  410 + f = open(filename, 'r')
  411 +
  412 + # Signal code block
  413 + code = False
  414 +
  415 + # Loop over lines
  416 + for line in f:
  417 +
  418 + # If we are not in a code block then check whether a code block
  419 + # starts
  420 + if not code:
  421 +
  422 + # Check for start of a code block
  423 + pos = line.find('code-block:: bash')
  424 + if pos != -1:
  425 + code = True
  426 + start = -1
  427 + tool = ''
  428 + lines = []
  429 + continue
  430 +
  431 + # ... otherwise check for literalinclude, and if found, copy
  432 + # relevant file
  433 + pos = line.find('literalinclude::')
  434 + if pos != -1:
  435 + filename = gammalib.strip_whitespace(line[pos+16:-1])
  436 + self._copy_file(filename)
  437 + continue
  438 +
  439 + # ... otherwise
  440 + else:
  441 +
  442 + # If we have no tool name then find tool name
  443 + if tool == '':
  444 + pos = line.find(' $ ')
  445 + if pos != -1:
  446 + start = pos+1
  447 + tool = line[pos+3:-1]
  448 + continue
  449 +
  450 + # ... otherwise append lines or test step
  451 + else:
  452 + blank = gammalib.strip_whitespace(line[0:start])
  453 + cmd = line[start:-1]
  454 + if len(blank) == 0 and cmd[0] != ' ':
  455 + lines.append(cmd)
  456 + continue
  457 + else:
  458 + self._test_step(tool, lines)
  459 + code = False
  460 + continue
  461 +
  462 + # Test step if some code is pending
  463 + if code:
  464 + self._test_step(tool, lines)
  465 +
  466 + # Return
  467 + return
  468 +
  469 + # Set test functions
  470 + def set(self):
  471 + """
  472 + Set all test functions
  473 + """
  474 + # Set current working directory
  475 + self._cwd = os.path.abspath(os.getcwd())
  476 +
  477 + # Set test name
  478 + self.name('Tutorials Verification')
  479 +
  480 + # Append tutorials
  481 + self.append(self.tutorials_quickstart, 'Test quickstart tutorial')
  482 + self.append(self.tutorials_howto, 'Test howto tutorial')
  483 + self.append(self.tutorials_1dc, 'Test 1DC tutorial')
  484 +
  485 + # Return
  486 + return
  487 +
  488 + # Test quickstart tutorials
  489 + def tutorials_quickstart(self):
  490 + """
  491 + Test quickstart tutorials
  492 + """
  493 + # Set Sphinx rst file path
  494 + os.chdir(self._cwd)
  495 + path = os.path.abspath('../doc/source/users/tutorials/quickstart')
  496 +
  497 + # Set result directory
  498 + self._set_result_dir('tutorials/quickstart')
  499 +
  500 + # Clean pfiles
  501 + self._clean_pfiles()
  502 +
  503 + # Initialise step counter
  504 + self._rst_step = 0
  505 +
  506 + # Test Sphinx rst files
  507 + self._test_rst_file('%s/simulating.rst' % path)
  508 + self._test_rst_file('%s/selecting.rst' % path)
  509 + self._test_rst_file('%s/skymap.rst' % path)
  510 + self._test_rst_file('%s/binning.rst' % path)
  511 + self._test_rst_file('%s/response.rst' % path)
  512 + self._test_rst_file('%s/fitting.rst' % path)
  513 + self._test_rst_file('%s/residual_map.rst' % path)
  514 + self._test_rst_file('%s/residual_spectrum.rst' % path)
  515 + self._test_rst_file('%s/butterfly.rst' % path)
  516 + self._test_rst_file('%s/spectrum.rst' % path)
  517 + self._test_rst_file('%s/unbinned.rst' % path)
  518 + self._test_rst_file('%s/energy_dispersion.rst' % path)
  519 + self._test_rst_file('%s/onoff.rst' % path)
  520 +
  521 + # Reset working directory
  522 + os.chdir(self._cwd)
  523 +
  524 + # Return
  525 + return
  526 +
  527 + # Test 1DC tutorials
  528 + def tutorials_1dc(self):
  529 + """
  530 + Test 1DC tutorials
  531 + """
  532 + # Continue only if CTADATA1DC environment variable is set
  533 + if 'CTADATA1DC' in os.environ:
  534 +
  535 + # Set environment variables
  536 + os.environ['CTADATA'] = os.environ['CTADATA1DC']
  537 + os.environ['CALDB'] = os.environ['CTADATA1DC']+'/caldb'
  538 +
  539 + # Set Sphinx rst file paths
  540 + os.chdir(self._cwd)
  541 + path_1dc = os.path.abspath('../doc/source/users/tutorials/1dc')
  542 + path_howto = os.path.abspath('../doc/source/users/tutorials/howto')
  543 +
  544 + # Set result directory
  545 + self._set_result_dir('tutorials/1dc')
  546 +
  547 + # Clean pfiles
  548 + self._clean_pfiles()
  549 +
  550 + # Initialise step counter
  551 + self._rst_step = 0
  552 +
  553 + # Test Sphinx rst files
  554 + self._test_rst_file('%s/first_select_obs.rst' % path_1dc)
  555 + self._test_rst_file('%s/first_select_events.rst' % path_1dc)
  556 + self._test_rst_file('%s/first_skymap.rst' % path_1dc)
  557 + self._test_rst_file('%s/first_detect.rst' % path_1dc)
  558 + self._test_rst_file('%s/first_stacked.rst' % path_1dc)
  559 + self._test_rst_file('%s/first_fitting.rst' % path_1dc)
  560 + self._test_rst_file('%s/first_improving.rst' % path_1dc)
  561 + self._test_rst_file('%s/first_unbinned.rst' % path_1dc)
  562 +
  563 + # Test Sphinx rst files
  564 + self._test_rst_file('%s/howto_ts.rst' % path_howto)
  565 + self._test_rst_file('%s/howto_tsmap.rst' % path_howto)
  566 + self._test_rst_file('%s/howto_extent.rst' % path_howto)
  567 + self._test_rst_file('%s/howto_ulimit.rst' % path_howto)
  568 + self._test_rst_file('%s/howto_lightcurve.rst' % path_howto)
  569 + self._test_rst_file('%s/howto_phasecurve.rst' % path_howto)
  570 + self._test_rst_file('%s/howto_exclude.rst' % path_howto)
  571 +
  572 + # Reset working directory
  573 + os.chdir(self._cwd)
  574 +
  575 + # Clean pfiles
  576 + self._clean_pfiles()
  577 +
  578 + # Initialise step counter
  579 + self._rst_step = 0
  580 +
  581 + # Test Sphinx rst file for On/Off analysis
  582 + self._test_rst_file('%s/first_onoff.rst' % path_1dc)
  583 +
  584 + # Reset working directory
  585 + os.chdir(self._cwd)
  586 +
  587 + # Return
  588 + return
  589 +
  590 + # Test howto tutorials
  591 + def tutorials_howto(self):
  592 + """
  593 + Test howto tutorials
  594 + """
  595 + # Set Sphinx rst file path
  596 + os.chdir(self._cwd)
  597 + path = os.path.abspath('../doc/source/users/tutorials/howto')
  598 +
  599 + # Set result directory
  600 + self._set_result_dir('tutorials/howto')
  601 +
  602 + # Initialise step counter
  603 + self._rst_step = 0
  604 +
  605 + # Test Sphinx rst files
  606 + self._clean_pfiles()
  607 + self._test_rst_file('%s/howto_connect_irfs.rst' % path)
  608 + self._clean_pfiles()
  609 + self._test_rst_file('%s/howto_xspec.rst' % path)
  610 +
  611 + # Reset working directory
  612 + os.chdir(self._cwd)
  613 +
  614 + # Return
  615 + return
  616 +
  617 +
  618 +
  619 +# ======================== #
  620 +# Main routine entry point #
  621 +# ======================== #
  622 +if __name__ == '__main__':
  623 +
  624 + # Allocate test suite container
  625 + suites = gammalib.GTestSuites('ctools tutorials verification')
  626 +
  627 + # Allocate test suite and append it to the container
  628 + suite_tutorials = tutorials()
  629 +
  630 + # Setup test suit
  631 + suite_tutorials.set()
  632 +
  633 + # Append test suite to container
  634 + suites.append(suite_tutorials)
  635 +
  636 + # Create tutorials/pfiles directory
  637 + try:
  638 + os.makedirs('tutorials/pfiles')
  639 + except:
  640 + pass
  641 +
  642 + # Set PFILES environment variable
  643 + os.environ['PFILES'] = os.path.abspath('tutorials/pfiles')
  644 +
  645 + # Get current working directory
  646 + cwd = os.getcwd()
  647 +
  648 + # Run test suite
  649 + success = suites.run()
  650 +
  651 + # Reset working directory
  652 + os.chdir(cwd)
  653 +
  654 + # Save test results
  655 + suites.save('reports/tutorials.xml')
  656 +
  657 + # Set return code
  658 + if success:
  659 + rc = 0
  660 + else:
  661 + rc = 1
  662 +
  663 + # Exit with return code
  664 + sys.exit(rc)
... ...