Commit e7db1c12bd3f4221a337eb9d9bd34092eea7927a

Authored by Jürgen Knödlseder
1 parent c5b2ebf7

Implement a new method to compute the butterfly diagram (#1498).

According to a study by Stephan Fegan done in the context of Fermi/LAT analysis, the best way to determine a butterfly diagram is to draw the envelope of the error ellipse of the model parameters. I implemented this method now in ctbutterfly. I also changed the output file, having now two columns giving as function of energy th minimum and maximum intensities.
doc/source/reference_manual/ctbutterfly.rst
... ... @@ -9,8 +9,15 @@ Computes butterfly.
9 9 Synopsis
10 10 --------
11 11  
12   -Calculates the confidence band of a spectral model, taking into account the
13   -covariance matrix of a likelihood fit.
  12 +Calculates the confidence band of a power law model, taking into account the
  13 +covariance matrix of a likelihood fit. The tool derives the envelope of
  14 +power law models with parameters that lie within the error ellipse. It
  15 +outputs an ASCII file in column separated value (CSV) format containing
  16 +the results. Each row in the result file corresponds to an energy. The
  17 +columns give the energy in MeV, the fitted intensity in ph/cm2/s/MeV,
  18 +and the minimum and maximum intensity that envelope the power law model
  19 +with a given confidence (by default a confidence level of 68% is used, but
  20 +the level can be adjusted using the ``confidence`` parameter).
14 21  
15 22  
16 23 General parameters
... ... @@ -45,7 +52,13 @@ General parameters
45 52  
46 53 ``outfile [file]``
47 54 Output butterfly ASCII file name.
  55 +
  56 +``(refit = no) [boolean]``
  57 + Performs refitting of solution ignoring any provided covariance matrix.
48 58  
  59 +``(confidence = 0.68) [real]``
  60 + Confidence level for error computation.
  61 +
49 62 ``(matrix = "NONE") [file]``
50 63 Input covariance matrix file (not used)
51 64  
... ... @@ -89,11 +102,12 @@ Standard parameters
89 102 ``(mode = ql) [string]``
90 103 Mode of automatic parameters (default is "ql", i.e. "query and learn").
91 104  
92   -``(logfile = ctskymap.log) [string]``
  105 +``(logfile = ctbutterfly.log) [string]``
93 106 Name of log file.
94 107  
95 108  
96 109 Related tools
97 110 -------------
98 111  
99   -None
  112 +:ref:`ctulimit`
  113 +:ref:`cterror`
... ...
doc/source/reference_manual/cterror.rst
... ... @@ -89,4 +89,5 @@ Standard parameters
89 89 Related tools
90 90 -------------
91 91  
92   -:ref:`ctulimit`
93 92 \ No newline at end of file
  93 +:ref:`ctulimit`
  94 +:ref:`ctbutterfly`
... ...
doc/source/reference_manual/ctlike.rst
... ... @@ -12,11 +12,10 @@ Synopsis
12 12 Determines source model parameters, such as flux, spectral index, source
13 13 position, and source extent from a maximum likelihood analysis of CTA data.
14 14 The analysis can be done in a binned or an unbinned formulation of the
15   -log-likelihood function.
16   -For binned analysis, a counts cube produced by :doc:`ctbin` is required.
17   -For unbinned analysis, an event list processed by :doc:`ctselect` is required.
18   -Based on the input file format, ctlike automatically selects between binned
19   -and unbinned maximum likelihood analysis.
  15 +log-likelihood function. For binned analysis, a counts cube produced by
  16 +:doc:`ctbin` is required. For unbinned analysis, an event list processed by
  17 +:doc:`ctselect` is required. Based on the input file format, ctlike
  18 +automatically selects between binned and unbinned maximum likelihood analysis.
20 19  
21 20  
22 21 General parameters
... ...
examples/show_butterfly.py
... ... @@ -18,7 +18,6 @@
18 18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 19 #
20 20 # ==========================================================================
21   -
22 21 import gammalib
23 22 import sys
24 23 try:
... ... @@ -26,32 +25,34 @@ try:
26 25 except:
27 26 sys.exit("This script needs matplotlib")
28 27  
29   -# =============== #
30   -# Script entry #
31   -# =============== #
32   -if __name__ == "__main__":
33 28  
34   - # check for given butterfly file
  29 +# ============ #
  30 +# Script entry #
  31 +# ============ #
  32 +if __name__ == "__main__":
  33 + """
  34 + """
  35 + # Check for given butterfly file
35 36 if not len(sys.argv) == 2:
36 37 sys.exit("Usage: show_butterfly.py butterfly.txt")
37 38  
38 39 # Read given butterfly file
39 40 filename = sys.argv[1]
40   - csv = gammalib.GCsv(filename)
  41 + csv = gammalib.GCsv(filename)
41 42  
42   - # initialise arrays to be filled
  43 + # Initialise arrays to be filled
43 44 butterfly_x = []
44 45 butterfly_y = []
  46 + line_x = []
  47 + line_y = []
45 48  
46   - line_x = []
47   - line_y = []
48   -
49   - # loop over rows of the file
  49 + # Loop over rows of the file
50 50 nrows = csv.nrows()
51 51 for row in range(nrows):
  52 +
52 53 # Compute upper edge of confidence band
53 54 butterfly_x.append(csv.real(row,0))
54   - butterfly_y.append(csv.real(row,1)+csv.real(row,2))
  55 + butterfly_y.append(csv.real(row,2))
55 56  
56 57 # Set line values
57 58 line_x.append(csv.real(row,0))
... ... @@ -61,21 +62,19 @@ if __name__ == &quot;__main__&quot;:
61 62 # of the confidence band
62 63 for row in range(nrows):
63 64 index = nrows - 1 - row
64   -
65 65 butterfly_x.append(csv.real(index,0))
66   - low_error = csv.real(index,1)-csv.real(index,2)
  66 + low_error = csv.real(index,3)
67 67 if low_error < 1e-26:
68 68 low_error = 1e-26
69 69 butterfly_y.append(low_error)
70 70  
71   - # plot the butterfly and spectral line
  71 + # Plot the butterfly and spectral line
72 72 plt.figure()
73 73 plt.ylim([1e-26,1e-14])
74 74 plt.loglog()
75 75 plt.grid()
76 76 plt.plot(line_x,line_y,color='black',ls='-')
77 77 plt.fill(butterfly_x,butterfly_y,color='green',alpha=0.5)
78   -
79   - plt.xlabel("Energy [MeV]")
80   - plt.ylabel(r"dN/dE [MeV$^{-1}$ s$^{-1}$ cm$^{-2}$]")
  78 + plt.xlabel("Energy (MeV)")
  79 + plt.ylabel(r"dN/dE (MeV$^{-1}$ s$^{-1}$ cm$^{-2}$)")
81 80 plt.show()
... ...
src/ctbutterfly/ctbutterfly.cpp
... ... @@ -36,7 +36,7 @@
36 36 /* __ Method name definitions ____________________________________________ */
37 37 #define G_GET_PARAMETERS "ctbutterfly::get_parameters()"
38 38 #define G_SAVE "ctbutterfly::save()"
39   -#define G_GET_MODEL_PARAMETER "ctbutterfly::get_model_parameter()"
  39 +#define G_CHECK_MODEL "ctbutterfly::check_model()"
40 40  
41 41 /* __ Debug definitions __________________________________________________ */
42 42  
... ... @@ -241,11 +241,40 @@ void ctbutterfly::run(void)
241 241 log << m_obs << std::endl;
242 242 }
243 243  
  244 + // If refit is selected then do maximum likelihood fit
  245 + if (m_refit) {
  246 +
  247 + // Write header
  248 + if (logTerse()) {
  249 + log << std::endl;
  250 + log.header1("Compute best-fit likelihood");
  251 + }
  252 +
  253 + // Optimize and compute errors
  254 + m_obs.optimize(m_opt);
  255 + m_obs.errors(m_opt);
  256 +
  257 + // Get covariance matrix by inverting the curvature matrix. Note that
  258 + // the curvature matrix is for the unscaled parameters, hence we have
  259 + // to scale them before we can proceed.
  260 + GObservations::likelihood likelihood = m_obs.function();
  261 + m_covariance = likelihood.curvature()->invert();
  262 +
  263 + // Write optimised model and covariance matrix into logger
  264 + if (logTerse()) {
  265 + log << m_opt << std::endl;
  266 + log << m_obs.models() << std::endl;
  267 + log << m_covariance << std::endl;
  268 + }
  269 +
  270 + } // endif: refit selected
  271 +
244 272 // Get model instance for further computations
245 273 GModels models = m_obs.models();
246 274  
247   - // Compute covariance matrix if necessary
248   - if (m_covariance.size() == 0) {
  275 + // If no refit is selected and no covariance matrix has been specified
  276 + // then compute now the covariance matrix
  277 + if (!m_refit && m_covariance.size() == 0) {
249 278  
250 279 // Write header
251 280 if (logTerse()) {
... ... @@ -258,101 +287,195 @@ void ctbutterfly::run(void)
258 287 GObservations::likelihood likelihood = m_obs.function();
259 288 likelihood.eval(pars);
260 289  
261   - // Get covariance matrix by inverting the curvature matrix
  290 + // Get covariance matrix by inverting the curvature matrix. Note that
  291 + // the curvature matrix is for the unscaled parameters, hence we have
  292 + // to scale them before we can proceed.
262 293 m_covariance = likelihood.curvature()->invert();
263 294  
264   - }
  295 + // Log covariance matrix
  296 + if (logTerse()) {
  297 + log << m_covariance << std::endl;
  298 + }
  299 +
  300 + } // endif: covariance computation needed
265 301  
266 302 // Write header
267 303 if (logTerse()) {
268 304 log << std::endl;
269   - log.header1("Generate butterfly");
  305 + log.header1("Prepare butterfly computation");
  306 + }
  307 +
  308 + // Find parameter indices. We do this by the memory location of the
  309 + // model parameters.
  310 + GModelSky* skymodel = dynamic_cast<GModelSky*>(models[m_srcname]);
  311 + GModelPar* par_prefactor = &(*skymodel)["Prefactor"];
  312 + GModelPar* par_index = &(*skymodel)["Index"];
  313 + int inx_prefactor = -1;
  314 + int inx_index = -1;
  315 + for (int i = 0; i < models.npars(); ++i) {
  316 + if (models.pars()[i] == par_prefactor) {
  317 + inx_prefactor = i;
  318 + }
  319 + if (models.pars()[i] == par_index) {
  320 + inx_index = i;
  321 + }
270 322 }
271 323  
272   - // Initialise dummy time to evaluate spectral model
273   - GTime time = GTime();
  324 + // Check indices
274 325  
275   - // Initialise vector of gradients
276   - GVector grad = GVector(m_obs.models().npars());
  326 + // Store model values and scales
  327 + double prefactor_mean = (*skymodel)["Prefactor"].value();
  328 + double index_mean = (*skymodel)["Index"].value();
  329 + double pivot_mean = (*skymodel)["PivotEnergy"].value();
  330 + double prefactor_scale = (*skymodel)["Prefactor"].scale();
  331 + double index_scale = (*skymodel)["Index"].scale();
  332 + GEnergy pivot(pivot_mean, "MeV");
277 333  
278   - // Loop over energy bins positions
279   - for (int i = 0; i < m_ebounds.size(); ++i) {
  334 + // Show parameter indices
  335 + if (logTerse()) {
  336 + log << gammalib::parformat("Prefactor");
  337 + log << prefactor_mean << " ph/cm2/s/MeV (";
  338 + log << inx_prefactor << ")" << std::endl;
  339 + log << gammalib::parformat("Index");
  340 + log << index_mean << " (" << inx_index << ")" << std::endl;
  341 + log << gammalib::parformat("Pivot energy");
  342 + log << pivot << std::endl;
  343 + }
280 344  
281   - // Initialise number of gradients
282   - int num_gradient = 0;
  345 + // Extract covariance elements
  346 + double cov_pp = m_covariance(inx_prefactor,inx_prefactor);
  347 + double cov_pg = m_covariance(inx_prefactor,inx_index);
  348 + double cov_gg = m_covariance(inx_index,inx_index);
283 349  
284   - // Get the energy of current bin
285   - GEnergy energy = m_ebounds.elogmean(i);
  350 + // Initialise min and max intensities
  351 + m_min_intensities.assign(m_ebounds.size(), 1e30);
  352 + m_max_intensities.assign(m_ebounds.size(), 0.0);
286 353  
287   - // Header for verbose logging
288   - if (logExplicit()) {
289   - std::string msg = "Computing butterfly for bin number "+
290   - gammalib::str(i)+" at "+energy.print();
291   - log << std::endl;
292   - log.header2(msg);
293   - }
  354 + // Compute eigenvectors and eigenvalues
  355 + double lambda1;
  356 + double lambda2;
  357 + GVector vector1(2);
  358 + GVector vector2(2);
  359 + eigenvectors(cov_pp, cov_pg, cov_pg, cov_gg,
  360 + &lambda1, &lambda2, &vector1, &vector2);
294 361  
295   - // Initialise flux value
296   - double model_flux = 0.0;
  362 + // Show eigenvectors and eigenvalues
  363 + if (logTerse()) {
  364 + log << gammalib::parformat("Eigenvalue 1") << lambda1 << std::endl;
  365 + log << gammalib::parformat("Eigenvalue 2") << lambda2 << std::endl;
  366 + log << gammalib::parformat("Eigenvector 1") << vector1 << std::endl;
  367 + log << gammalib::parformat("Eigenvector 2") << vector2 << std::endl;
  368 + }
297 369  
298   - // Loop over models
299   - for (int j = 0; j < models.size(); ++j) {
  370 + // Confidence scaling
  371 + double sigma = gammalib::erfinv(m_confidence) * gammalib::sqrt_two;
  372 + double scale = (sigma*sigma);
300 373  
301   - // If source name is found, retrieve the nominal flux
302   - if (models[j]->name() == m_srcname) {
  374 + // Show confidence stuff
  375 + if (logTerse()) {
  376 + log << gammalib::parformat("Confidence level");
  377 + log << m_confidence << std::endl;
  378 + log << gammalib::parformat("Corresponding scaling");
  379 + log << scale << std::endl;
  380 + }
303 381  
304   - // Note that we have to compute the gradients here
305   - // repetively to update the gradients in the vector
306   - // No check for valid pointers necessary here as this
307   - // was covered by get_model_parameter()
308   - GModelSky* skymodel = dynamic_cast<GModelSky*>(models[j]);
309   - GModelSpectral* spectral = skymodel->spectral();
310   - model_flux = spectral->eval_gradients(energy, time);
  382 + // Compute minor and major axes of error ellipse
  383 + double major = scale*std::sqrt(lambda1);
  384 + double minor = scale*std::sqrt(lambda2);
311 385  
312   - }
  386 + // Write header
  387 + if (logTerse()) {
  388 + log << std::endl;
  389 + log.header1("Generate butterfly");
  390 + }
313 391  
314   - // Loop over model parameters
315   - for (int i = 0; i < models[j]->size(); ++i) {
  392 + // Walk around the error ellipse
  393 + int nt = 360;
  394 + double dt = gammalib::twopi/double(nt);
  395 + double t = 0.0;
  396 + for (int i = 0; i < nt; ++i, t += dt) {
  397 +
  398 + // Pre-compute sin and cosine
  399 + double sin_t = std::sin(t);
  400 + double cos_t = std::cos(t);
  401 +
  402 + // Compute prefactor and index
  403 + double prefactor = (major * cos_t * vector1[0] +
  404 + minor * sin_t * vector2[0]) * prefactor_scale +
  405 + prefactor_mean;
  406 + double index = (major * cos_t * vector1[1] +
  407 + minor * sin_t * vector2[1]) * index_scale +
  408 + index_mean;
  409 +
  410 + // Show prefactor and index
  411 + if (logTerse()) {
  412 + log << gammalib::parformat("Angle "+gammalib::str(t*gammalib::rad2deg));
  413 + log << prefactor << " ph/cm2/s/MeV; ";
  414 + log << index << std::endl;
  415 + }
316 416  
317   - // Get model parameter
318   - GModelPar parameter = models[j]->at(i);
  417 + // Setup power law model
  418 + GModelSpectralPlaw plaw(prefactor, index, pivot);
319 419  
320   - // Set gradient value to the vector
321   - grad[num_gradient] = parameter.gradient();
  420 + // Evaluate power law at each energy and extract minimum and
  421 + // maximum intensity
  422 + for (int k = 0; k < m_ebounds.size(); ++k) {
322 423  
323   - // increment vector index
324   - num_gradient++;
  424 + // Get the energy of current bin
  425 + GTime time;
  426 + GEnergy energy = m_ebounds.elogmean(k);
325 427  
326   - } // endfor: Looped over model parameters
  428 + // Evaluate power law
  429 + double intensity = plaw.eval(energy, time);
  430 +
  431 + // Get extremes
  432 + if (intensity < m_min_intensities[k]) {
  433 + m_min_intensities[k] = intensity;
  434 + }
  435 + if (intensity > m_max_intensities[k]) {
  436 + m_max_intensities[k] = intensity;
  437 + }
  438 +
  439 + } // endfor: looped over all energies
  440 +
  441 + } // endfor: looped over all angles
  442 +
  443 + // Write header
  444 + if (logTerse()) {
  445 + log << std::endl;
  446 + log.header1("Results");
  447 + }
327 448  
328   - } // endfor: Looped over models
  449 + // Setup power law model
  450 + GModelSpectralPlaw plaw(prefactor_mean, index_mean, pivot);
329 451  
330   - // Multiply covariance to the gradient vector
331   - GVector vector = m_covariance * grad;
  452 + // Show results
  453 + for (int k = 0; k < m_ebounds.size(); ++k) {
332 454  
333   - // Get the error from the scalar product
334   - double error = std::sqrt(grad * vector);
  455 + // Get the energy of current bin
  456 + GTime time;
  457 + GEnergy energy = m_ebounds.elogmean(k);
335 458  
336   - // Multiply in scale of spectral parameter
337   - error *= m_par_scale;
  459 + // Evaluate power law
  460 + double intensity = plaw.eval(energy, time);
338 461  
339   - // Store flux, value and energy for saving
340   - m_fluxes.push_back(model_flux);
  462 + // Store results
341 463 m_energies.push_back(energy.MeV());
342   - m_errors.push_back(error);
  464 + m_intensities.push_back(intensity);
343 465  
344 466 // Log information
345   - if (logExplicit()) {
346   - log << " Flux at ";
347   - log << energy.print();
348   - log << ": ";
349   - log << model_flux;
350   - log << " +- ";
351   - log << error;
352   - log << " ph/cm2/s/MeV";
  467 + if (logTerse()) {
  468 + log << gammalib::parformat("Intensity at "+energy.print());
  469 + log << intensity;
  470 + log << " (";
  471 + log << m_min_intensities[k];
  472 + log << ", ";
  473 + log << m_max_intensities[k];
  474 + log << ") ph/cm2/s/MeV";
  475 + log << std::endl;
353 476 }
354 477  
355   - } //endfor: loop over energy bin
  478 + } // endfor
356 479  
357 480 // Restore energy dispersion flag for all CTA observations
358 481 for (int i = 0; i < m_obs.size(); ++i) {
... ... @@ -384,14 +507,15 @@ void ctbutterfly::save(void)
384 507 // Get output filename
385 508 m_outfile = (*this)["outfile"].filename();
386 509  
387   - // Create CSV table with 3 columns
388   - GCsv table(m_fluxes.size(), 3);
  510 + // Create CSV table with 4 columns
  511 + GCsv table(m_energies.size(), 4);
389 512  
390 513 // Fill CSV table
391   - for (int i = 0; i < m_fluxes.size(); ++i) {
  514 + for (int i = 0; i < m_energies.size(); ++i) {
392 515 table.real(i, 0, m_energies[i]);
393   - table.real(i, 1, m_fluxes[i]);
394   - table.real(i, 2, m_errors[i]);
  516 + table.real(i, 1, m_intensities[i]);
  517 + table.real(i, 2, m_min_intensities[i]);
  518 + table.real(i, 3, m_max_intensities[i]);
395 519 }
396 520  
397 521 // Save CSV table
... ... @@ -415,18 +539,25 @@ void ctbutterfly::init_members(void)
415 539 {
416 540 // Initialise members
417 541 m_srcname.clear();
418   - m_outfile.clear();
419   - m_ebounds.clear();
  542 + m_confidence = 0.68;
  543 + m_max_iter = 50;
420 544 m_apply_edisp = false;
  545 + m_refit = false;
  546 + m_ebounds.clear();
  547 + m_outfile.clear();
421 548  
422 549 // Initialise protected members
423   - m_skymodel = NULL;
424   - m_par_scale = 0.0;
  550 + m_opt.clear();
425 551 m_obs.clear();
426 552 m_covariance.clear();
427 553 m_energies.clear();
428   - m_fluxes.clear();
429   - m_errors.clear();
  554 + m_intensities.clear();
  555 + m_min_intensities.clear();
  556 + m_max_intensities.clear();
  557 +
  558 + // Set optimizer parameters
  559 + m_opt.max_iter(m_max_iter);
  560 + m_opt.max_stalls(10);
430 561  
431 562 // Return
432 563 return;
... ... @@ -441,20 +572,22 @@ void ctbutterfly::init_members(void)
441 572 void ctbutterfly::copy_members(const ctbutterfly& app)
442 573 {
443 574 // Copy attributes
444   - m_srcname = app.m_srcname;
445   - m_outfile = app.m_outfile;
446   - m_ebounds = app.m_ebounds;
  575 + m_srcname = app.m_srcname;
  576 + m_confidence = app.m_confidence;
  577 + m_max_iter = app.m_max_iter;
447 578 m_apply_edisp = app.m_apply_edisp;
448   -
  579 + m_refit = app.m_refit;
  580 + m_ebounds = app.m_ebounds;
  581 + m_outfile = app.m_outfile;
  582 +
449 583 // Copy protected members
450   - m_obs = app.m_obs;
451   - m_covariance = app.m_covariance;
452   - m_energies = app.m_energies;
453   - m_fluxes = app.m_fluxes;
454   - m_errors = app.m_errors;
455   -
456   - // Extract model parameter
457   - get_model_parameter();
  584 + m_obs = app.m_obs;
  585 + m_opt = app.m_opt;
  586 + m_covariance = app.m_covariance;
  587 + m_energies = app.m_energies;
  588 + m_intensities = app.m_intensities;
  589 + m_min_intensities = app.m_min_intensities;
  590 + m_max_intensities = app.m_max_intensities;
458 591  
459 592 // Return
460 593 return;
... ... @@ -513,15 +646,9 @@ void ctbutterfly::get_parameters(void)
513 646  
514 647 // Get name of test source and check container for this name
515 648 m_srcname = (*this)["srcname"].string();
516   - if (!m_obs.models().contains(m_srcname)) {
517   - std::string msg = "Source \""+m_srcname+"\" not found in model "
518   - "container. Please add a source with that name "
519   - "or check for possible typos.";
520   - throw GException::invalid_value(G_GET_PARAMETERS, msg);
521   - }
522 649  
523   - // Get relevant model and parameter for model computation
524   - get_model_parameter();
  650 + // Check model name and type
  651 + check_model();
525 652  
526 653 // Create energy boundaries from user parameters
527 654 m_ebounds = create_ebounds();
... ... @@ -539,6 +666,10 @@ void ctbutterfly::get_parameters(void)
539 666 // Read energy dispersion flag
540 667 m_apply_edisp = (*this)["edisp"].boolean();
541 668  
  669 + // Read other parameters
  670 + m_confidence = (*this)["confidence"].real();
  671 + m_refit = (*this)["refit"].boolean();
  672 +
542 673 // Optionally read ahead parameters so that they get correctly
543 674 // dumped into the log file
544 675 if (read_ahead()) {
... ... @@ -551,62 +682,114 @@ void ctbutterfly::get_parameters(void)
551 682  
552 683  
553 684 /***********************************************************************//**
554   - * @brief Get application parameters
  685 + * @brief Check if sky model is valid
555 686 *
556 687 * @exception GException::invalid_value
557   - * Did not find a valid model parameter
  688 + * Did not find a valid model
558 689 *
559   - * Extracts a pointer to the sky model (m_skymodel) and a pointer to the
560   - * relevant model parameter (m_model_par) from the model container.
  690 + * Checks whether the specified sky model exists and whether it is a power
  691 + * law.
561 692 ***************************************************************************/
562   -void ctbutterfly::get_model_parameter(void)
  693 +void ctbutterfly::check_model(void)
563 694 {
564 695 // Continue only if source model exists
565 696 if (m_obs.models().contains(m_srcname)) {
566 697  
567 698 // Get relevant model and parameter for upper limit computation.
568   - GModels& models = const_cast<GModels&>(m_obs.models());
569   - m_skymodel = dynamic_cast<GModelSky*>(models[m_srcname]);
570   - if (m_skymodel == NULL) {
  699 + GModels& models = const_cast<GModels&>(m_obs.models());
  700 + GModelSky* model = dynamic_cast<GModelSky*>(models[m_srcname]);
  701 + if (model == NULL) {
571 702 std::string msg = "Source \""+m_srcname+"\" is not a sky model. "
572 703 "Please specify the name of a sky model for "
573 704 "butterfly computation.";
574   - throw GException::invalid_value(G_GET_MODEL_PARAMETER, msg);
  705 + throw GException::invalid_value(G_CHECK_MODEL, msg);
575 706 }
576 707  
577   - GModelPar* modelpar;
578   -
579   - if (m_skymodel->spectral()->type() == "NodeFunction") {
580   - std::string msg = "\"NodeFunction\" cannot be used as spectral "
581   - "model for an butterfly computation. "
582   - "Please specify another spectral model.";
583   - throw GException::invalid_value(G_GET_MODEL_PARAMETER, msg);
584   - }
585   - if (m_skymodel->spectral()->has_par("Normalization")) {
586   - modelpar = &(m_skymodel->spectral()->operator[]("Normalization"));
587   - }
588   - else if (m_skymodel->spectral()->has_par("Prefactor")) {
589   - modelpar = &(m_skymodel->spectral()->operator[]("Prefactor"));
590   - }
591   - else if (m_skymodel->spectral()->has_par("Integral")) {
592   - modelpar = &(m_skymodel->spectral()->operator[]("Integral"));
593   - }
594   - else if (m_skymodel->spectral()->has_par("Value")) {
595   - modelpar = &(m_skymodel->spectral()->operator[]("Value"));
596   - }
597   - else {
598   - std::string msg = "Require spectral parameter \"Normalization\", "
599   - "\"Prefactor\", \"Integral\" or \"Value\" for "
600   - "butterfly computation. The specified source "
601   - "\""+m_srcname+"\" does not have such a parameter.";
602   - throw GException::invalid_value(G_GET_MODEL_PARAMETER, msg);
  708 + // Check that the spectral model is a power law
  709 + if (model->spectral()->type() != "PowerLaw") {
  710 + std::string msg = "\""+model->spectral()->type()+"\" cannot be "
  711 + "used as spectral model for an butterfly "
  712 + "computation. Please specify a power law model.";
  713 + throw GException::invalid_value(G_CHECK_MODEL, msg);
603 714 }
604 715  
605   - // Set scale value from parameter
606   - m_par_scale = modelpar->scale();
607   -
608 716 } // endif: source model existed
609 717  
  718 + // Otherwise throw an exception
  719 + else {
  720 + std::string msg = "Source \""+m_srcname+"\" not found in model "
  721 + "container. Please add a source with that name "
  722 + "or check for possible typos.";
  723 + throw GException::invalid_value(G_CHECK_MODEL, msg);
  724 + }
  725 +
  726 + // Return
  727 + return;
  728 +}
  729 +
  730 +
  731 +/***********************************************************************//**
  732 + * @brief Compute normalized eigenvectors and eigenvalues
  733 + *
  734 + * @param[in] a Covariance matrix element (0,0)
  735 + * @param[in] b Covariance matrix element (0,1)
  736 + * @param[in] c Covariance matrix element (1,0)
  737 + * @param[in] d Covariance matrix element (1,1)
  738 + * @param[out] lambda1 First eigenavlue
  739 + * @param[out] lambda2 Second eigenavlue
  740 + * @param[out] vector1 First eigenvector (normalized)
  741 + * @param[out] vector2 Second eigenvector (normalized)
  742 + *
  743 + * Computes the eigenvalues and eigenvectors for a 2x2 matrix. See
  744 + * http://www.math.harvard.edu/archive/21b_fall_04/exhibits/2dmatrices/index.html
  745 + ***************************************************************************/
  746 +void ctbutterfly::eigenvectors(const double& a,
  747 + const double& b,
  748 + const double& c,
  749 + const double& d,
  750 + double* lambda1,
  751 + double* lambda2,
  752 + GVector* vector1,
  753 + GVector* vector2)
  754 +{
  755 + // Compute trace and determinant
  756 + double trace = a + d;
  757 + double det = a*d - b*c;
  758 +
  759 + // Compute eigenvalues
  760 + double arg1 = 0.5*trace;
  761 + double arg2 = std::sqrt(0.25*trace*trace - det);
  762 + *lambda1 = arg1 + arg2;
  763 + *lambda2 = arg1 - arg2;
  764 +
  765 + // Compute eigenvectors
  766 + double vector1_1;
  767 + double vector1_2;
  768 + double vector2_1;
  769 + double vector2_2;
  770 + if (c != 0.0) {
  771 + (*vector1)[0] = *lambda1 - det;
  772 + (*vector1)[1] = c;
  773 + (*vector2)[0] = *lambda2 - det;
  774 + (*vector2)[1] = c;
  775 + }
  776 + else if (b != 0.0) {
  777 + (*vector1)[0] = b;
  778 + (*vector1)[1] = *lambda1 - a;
  779 + (*vector2)[0] = b;
  780 + (*vector2)[1] = *lambda2 - a;
  781 + }
  782 + else {
  783 + (*vector1)[0] = 1.0;
  784 + (*vector1)[1] = 0.0;
  785 + (*vector2)[0] = 0.0;
  786 + (*vector2)[1] = 1.0;
  787 + }
  788 +
  789 + // Normalize eigenvectors
  790 + *vector1 /= norm(*vector1);
  791 + *vector2 /= norm(*vector2);
  792 +
610 793 // Return
611 794 return;
612 795 }
... ...
src/ctbutterfly/ctbutterfly.hpp
... ... @@ -78,22 +78,33 @@ protected:
78 78 void copy_members(const ctbutterfly& app);
79 79 void free_members(void);
80 80 void get_parameters(void);
81   - void get_model_parameter(void);
  81 + void check_model(void);
  82 + void eigenvectors(const double& a,
  83 + const double& b,
  84 + const double& c,
  85 + const double& d,
  86 + double* lambda1,
  87 + double* lambda2,
  88 + GVector* vector1,
  89 + GVector* vector2);
82 90  
83 91 // User parameters
84   - std::string m_srcname; //!< Name of source to compute butterfly
85   - std::string m_outfile; //!< Output ascii file
86   - GEbounds m_ebounds; //!< Energy binning definition
87   - bool m_apply_edisp; //!< Apply energy dispersion?
  92 + std::string m_srcname; //!< Name of source to compute butterfly
  93 + double m_confidence; //!< Confidence level
  94 + int m_max_iter; //!< Maximum number of iterations
  95 + bool m_apply_edisp; //!< Apply energy dispersion?
  96 + bool m_refit; //!< Do refit?
  97 + GEbounds m_ebounds; //!< Energy binning definition
  98 + std::string m_outfile; //!< Output ascii file
88 99  
89 100 // Protected members
90   - GObservations m_obs; //!< Observation container
91   - GMatrixSparse m_covariance; //!< Covariance matrix
92   - GModelSky* m_skymodel; //!< Pointer to sky model
93   - double m_par_scale; //!< Scale of normalising parameter
94   - std::vector<double> m_energies; //!< Energy values for storage
95   - std::vector<double> m_fluxes; //!< Flux values per energy bin
96   - std::vector<double> m_errors; //!< Flux errors per energy bin
  101 + GOptimizerLM m_opt; //!< Optimizer
  102 + GObservations m_obs; //!< Observation container
  103 + GMatrixSparse m_covariance; //!< Covariance matrix
  104 + std::vector<double> m_energies; //!< Energy values
  105 + std::vector<double> m_intensities; //!< Power law intensity
  106 + std::vector<double> m_min_intensities; //!< Minimum intensities
  107 + std::vector<double> m_max_intensities; //!< Maximum intensities
97 108 };
98 109  
99 110 /***********************************************************************//**
... ...
src/ctbutterfly/ctbutterfly.par
... ... @@ -17,17 +17,19 @@
17 17 #
18 18 # General parameters
19 19 #===================
20   -inobs, f, a, "events.fits",,,"Input event list, counts cube or observation definition file"
21   -inmodel, f, a, "$CTOOLS/share/models/crab.xml",,, "Source model"
22   -srcname, s, a, "Crab",,,"Source of interest"
23   -expcube, f, a, "NONE",,, "Exposure cube file (only needed for stacked analysis)"
24   -psfcube, f, a, "NONE",,, "PSF cube file (only needed for stacked analysis)"
25   -bkgcube, f, a, "NONE",,, "Background cube file (only needed for stacked analysis)"
26   -caldb, s, a, "prod2",,, "Calibration database"
27   -irf, s, a, "South_50h",,, "Instrument response function"
28   -edisp, b, h, no,,, "Apply energy dispersion?"
29   -outfile, f, a, "butterfly.txt",,, "Output ascii file"
30   -matrix, f, h, "NONE",,, "Input covariance Matrix FITS file"
  20 +inobs, f, a, "events.fits",,,"Input event list, counts cube or observation definition file"
  21 +inmodel, f, a, "$CTOOLS/share/models/crab.xml",,, "Source model"
  22 +srcname, s, a, "Crab",,,"Source of interest"
  23 +expcube, f, a, "NONE",,, "Exposure cube file (only needed for stacked analysis)"
  24 +psfcube, f, a, "NONE",,, "PSF cube file (only needed for stacked analysis)"
  25 +bkgcube, f, a, "NONE",,, "Background cube file (only needed for stacked analysis)"
  26 +caldb, s, a, "prod2",,, "Calibration database"
  27 +irf, s, a, "South_50h",,, "Instrument response function"
  28 +edisp, b, h, no,,, "Apply energy dispersion?"
  29 +outfile, f, a, "butterfly.txt",,, "Output ascii file"
  30 +refit, b, h, no,,, "Do refitting?"
  31 +confidence, r, h, 0.68,0.0,1.0, "Confidence level"
  32 +matrix, f, h, "NONE",,, "Input covariance Matrix FITS file"
31 33  
32 34 #
33 35 # Energy binning parameters
... ...
src/cterror/cterror.par
... ... @@ -6,7 +6,7 @@
6 6 # ------------------------------------------------------------------------- #
7 7 # #
8 8 # File: cterror.par #
9   -# Version: 01-00-00 #
  9 +# Version: 1.0.0 #
10 10 # #
11 11 # Author: Florent Forest #
12 12 # florent.forest@isae.fr #
... ...