ctools  2.0.0
 All Classes Namespaces Files Functions Variables Macros Pages
ctbutterfly.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * ctbutterfly - butterfly calculation tool *
3  * ----------------------------------------------------------------------- *
4  * copyright (C) 2014-2022 by Michael Mayer *
5  * ----------------------------------------------------------------------- *
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 /**
22  * @file ctbutterfly.cpp
23  * @brief Butterfly computation tool
24  * @author Michael Mayer
25  */
26 
27 /* __ Includes ___________________________________________________________ */
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 #include <cstdio>
32 #include <fstream>
33 #include "ctbutterfly.hpp"
34 #include "GTools.hpp"
35 
36 /* __ Method name definitions ____________________________________________ */
37 #define G_GET_PARAMETERS "ctbutterfly::get_parameters()"
38 #define G_SAVE "ctbutterfly::save()"
39 #define G_CHECK_MODEL "ctbutterfly::check_model()"
40 
41 /* __ Debug definitions __________________________________________________ */
42 
43 /* __ Coding definitions _________________________________________________ */
44 
45 
46 /*==========================================================================
47  = =
48  = Constructors/destructors =
49  = =
50  ==========================================================================*/
51 
52 /***********************************************************************//**
53  * @brief Void constructor
54  ***************************************************************************/
56 {
57  // Initialise members
58  init_members();
59 
60  // Return
61  return;
62 }
63 
64 
65 /***********************************************************************//**
66  * @brief Observations constructor
67  *
68  * param[in] obs Observation container.
69  *
70  * This method creates an instance of the class by copying an existing
71  * observations container.
72  ***************************************************************************/
73 ctbutterfly::ctbutterfly(const GObservations& obs) :
74  ctlikelihood(CTBUTTERFLY_NAME, VERSION, obs)
75 {
76  // Initialise members
77  init_members();
78 
79  // Return
80  return;
81 }
82 
83 
84 
85 /***********************************************************************//**
86  * @brief Command line constructor
87  *
88  * @param[in] argc Number of arguments in command line.
89  * @param[in] argv Array of command line arguments.
90  ***************************************************************************/
91 ctbutterfly::ctbutterfly(int argc, char *argv[]) :
92  ctlikelihood(CTBUTTERFLY_NAME, VERSION, argc, argv)
93 {
94  // Initialise members
95  init_members();
96 
97  // Return
98  return;
99 }
100 
101 
102 /***********************************************************************//**
103  * @brief Copy constructor
104  *
105  * @param[in] app Application.
106  ***************************************************************************/
108 {
109  // Initialise members
110  init_members();
111 
112  // Copy members
113  copy_members(app);
114 
115  // Return
116  return;
117 }
118 
119 
120 /***********************************************************************//**
121  * @brief Destructor
122  ***************************************************************************/
124 {
125  // Free members
126  free_members();
127 
128  // Return
129  return;
130 }
131 
132 
133 /*==========================================================================
134  = =
135  = Operators =
136  = =
137  ==========================================================================*/
138 
139 /***********************************************************************//**
140  * @brief Assignment operator
141  *
142  * @param[in] app Application.
143  * @return Application.
144  ***************************************************************************/
146 {
147  // Execute only if object is not identical
148  if (this != &app) {
149 
150  // Copy base class members
151  this->ctlikelihood::operator=(app);
152 
153  // Free members
154  free_members();
155 
156  // Initialise members
157  init_members();
158 
159  // Copy members
160  copy_members(app);
161 
162  } // endif: object was not identical
163 
164  // Return this object
165  return *this;
166 }
167 
168 
169 /*==========================================================================
170  = =
171  = Public methods =
172  = =
173  ==========================================================================*/
174 
175 /***********************************************************************//**
176  * @brief Clear ctbutterfly tool
177  *
178  * Clears ctbutterfly tool.
179  ***************************************************************************/
181 {
182  // Free members
183  free_members();
186  this->ctool::free_members();
187 
188  // Clear base class (needed to conserve tool name and version)
189  this->GApplication::clear();
190 
191  // Initialise members
192  this->ctool::init_members();
195  init_members();
196 
197  // Write header into logger
198  log_header();
199 
200  // Return
201  return;
202 }
203 
204 
205 /***********************************************************************//**
206  * @brief Computes the butterfly
207  *
208  * This method takes the spectral fit and its covariance matrix to compute
209  * the confidence band via Gaussian error propagation
210  ***************************************************************************/
212 {
213  // Get task parameters
214  get_parameters();
215 
216  // Set energy dispersion flag for all CTA observations and save old
217  // values in save_edisp vector
218  std::vector<bool> save_edisp;
219  save_edisp.assign(m_obs.size(), false);
220  for (int i = 0; i < m_obs.size(); ++i) {
221  GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
222  if (obs != NULL) {
223  save_edisp[i] = obs->response()->apply_edisp();
224  obs->response()->apply_edisp(m_apply_edisp);
225  }
226  }
227 
228  // Write input observation container into logger
229  log_observations(NORMAL, m_obs, "Input observation");
230 
231  // If fit is selected then do maximum likelihood fit
232  if (m_fit) {
233 
234  // Write header into logger
235  log_header1(TERSE, "Compute best-fit likelihood");
236 
237  // Optimize and compute errors
238  m_obs.optimize(m_opt);
239  m_obs.errors(m_opt);
240 
241  // Get covariance matrix. For the envelope method we need an unscaled
242  // matrix, hence we directly access the curvature matrix and invert
243  // it
244  GObservations::likelihood likelihood = m_obs.function();
245  if (m_method == "GAUSSIAN") {
246  m_covariance = likelihood.covariance();
247  }
248  else {
249  m_covariance = likelihood.curvature()->invert();
250  }
251 
252  // Write optimizer, optimised model and covariance matrix into logger
253  log_string(NORMAL, m_opt.print(m_chatter));
254  log_string(NORMAL, m_obs.models().print(m_chatter));
255  log_string(EXPLICIT, m_covariance.print(m_chatter));
256 
257  } // endif: fit selected
258 
259  // Get model instance for further computations
260  GModels models = m_obs.models();
261 
262  // If no fit is selected and no covariance matrix has been specified
263  // then compute now the covariance matrix
264  if (!m_fit && m_covariance.size() == 0) {
265 
266  // Write header into logger
267  log_header1(TERSE, "Compute covariance matrix");
268 
269  // Evaluate curvature matrix at the actual parameters
270  GOptimizerPars pars = models.pars();
271  GObservations::likelihood likelihood = m_obs.function();
272  likelihood.eval(pars);
273 
274  // Get covariance matrix. For the envelope method we need an unscaled
275  // matrix, hence we directly access the curvature matrix and invert
276  // it
277  if (m_method == "GAUSSIAN") {
278  m_covariance = likelihood.covariance();
279  }
280  else {
281  m_covariance = likelihood.curvature()->invert();
282  }
283 
284  // Write covariance matrix into logger
285  log_string(EXPLICIT, m_covariance.print(m_chatter));
286 
287  } // endif: covariance computation needed
288 
289  // Compute butterfly depending on selected method
290  if (m_method == "GAUSSIAN") {
292  }
293  else {
294  ellipsoid_boundary(models);
295  }
296 
297  // Create FITS table
298  create_fits();
299 
300  // Write header into logger
301  log_header1(TERSE, "Results");
302 
303  // Show results by converting from MeV to TeV
304  for (int k = 0; k < m_ebounds.size(); ++k) {
305 
306  // Write message
307  std::string key = "Intensity at " +
308  gammalib::str(m_energies[k]*1.0e-6) +
309  " TeV";
310  std::string value = gammalib::str(m_intensities[k]*1.0e6) + " (" +
311  gammalib::str(m_min_intensities[k]*1.0e6) + ", " +
312  gammalib::str(m_max_intensities[k]*1.0e6) +
313  ") ph/cm2/s/TeV";
314 
315  // Write results into logger
316  log_value(NORMAL, key, value);
317 
318  } // endfor: loop over energy bin
319 
320  // Restore energy dispersion flag for all CTA observations
321  for (int i = 0; i < m_obs.size(); ++i) {
322  GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
323  if (obs != NULL) {
324  obs->response()->apply_edisp(save_edisp[i]);
325  }
326  }
327 
328  // Return
329  return;
330 }
331 
332 
333 /***********************************************************************//**
334  * @brief Save butterfly diagram
335  *
336  * Saves the butterfly diagram FITS file.
337  ***************************************************************************/
339 {
340  // Write header into logger
341  log_header1(TERSE, "Save Butterfly diagram");
342 
343  // Save only if filename is valid
344  if ((*this)["outfile"].is_valid()) {
345 
346  // Get output filename
347  m_outfile = (*this)["outfile"].filename();
348 
349  // Log butterfly diagram file name
350  log_value(NORMAL, "Butterfly file", m_outfile.url());
351 
352  // Save FITS table
353  m_fits.saveto(m_outfile.url(), clobber());
354 
355  }
356 
357  // Return
358  return;
359 }
360 
361 
362 /*==========================================================================
363  = =
364  = Private methods =
365  = =
366  ==========================================================================*/
367 
368 /***********************************************************************//**
369  * @brief Initialise class members
370  ***************************************************************************/
372 {
373  // Initialise members
374  m_srcname.clear();
375  m_method.clear();
376  m_confidence = 0.68;
377  m_apply_edisp = false;
378  m_fit = false;
379  m_chatter = static_cast<GChatter>(2);
380  m_ebounds.clear();
381  m_outfile.clear();
382 
383  // Initialise protected members
384  m_covariance.clear();
385  m_energies.clear();
386  m_intensities.clear();
387  m_min_intensities.clear();
388  m_max_intensities.clear();
389  m_fits.clear();
390 
391  // Return
392  return;
393 }
394 
395 
396 /***********************************************************************//**
397  * @brief Copy class members
398  *
399  * @param[in] app Application.
400  ***************************************************************************/
402 {
403  // Copy attributes
404  m_srcname = app.m_srcname;
405  m_method = app.m_method;
407  m_max_iter = app.m_max_iter;
409  m_fit = app.m_fit;
410  m_chatter = app.m_chatter;
411  m_ebounds = app.m_ebounds;
412  m_outfile = app.m_outfile;
413 
414  // Copy protected members
416  m_energies = app.m_energies;
420  m_fits = app.m_fits;
421 
422  // Return
423  return;
424 }
425 
426 
427 /***********************************************************************//**
428  * @brief Delete class members
429  ***************************************************************************/
431 {
432  // Return
433  return;
434 }
435 
436 
437 /***********************************************************************//**
438  * @brief Get application parameters
439  *
440  * @exception GException::feature_not_implemented
441  * Loading of covariance matrix is not yet implemented.
442  *
443  * Get all task parameters from parameter file.
444  ***************************************************************************/
446 {
447  // Setup observations from "inobs" parameter
449 
450  // Set observation statistic
451  set_obs_statistic(gammalib::toupper((*this)["statistic"].string()));
452 
453  // Setup models from "inmodel" parameter
454  setup_models(m_obs, (*this)["srcname"].string());
455 
456  // Get name of test source
457  m_srcname = (*this)["srcname"].string();
458 
459  // Create energy boundaries from user parameters
461 
462  // Get matrix file name and load if possible
463  if ((*this)["matrix"].is_valid()) {
464  std::string msg = "Loading of matrix from file not implemented yet. "
465  "Use filename = \"NONE\" to induce a recomputation "
466  "of the matrix internally.";
467  throw GException::feature_not_implemented(G_GET_PARAMETERS, msg);
468  // m_covariance.load(matrixfilename);
469  }
470 
471  // Set optimizer characteristics from user parameters
472  m_opt.eps((*this)["like_accuracy"].real());
473  m_opt.max_iter((*this)["max_iter"].integer());
474 
475  // Get remaining parameters
476  m_apply_edisp = (*this)["edisp"].boolean();
477  m_method = (*this)["method"].string();
478  m_confidence = (*this)["confidence"].real();
479  m_fit = (*this)["fit"].boolean();
480  m_chatter = static_cast<GChatter>((*this)["chatter"].integer());
481 
482  // Check model name and type
483  check_model();
484 
485  // If needed later, query output filename now
486  if (read_ahead()) {
487  (*this)["outfile"].query();
488  }
489 
490  // Write parameters into logger
491  log_parameters(TERSE);
492 
493  // Return
494  return;
495 }
496 
497 
498 /***********************************************************************//**
499  * @brief Compute butterfly using the ellipsoid boundary method
500  *
501  * @param[in] models Models.
502  ***************************************************************************/
503 void ctbutterfly::ellipsoid_boundary(GModels& models)
504 {
505  // Write header into logger
506  log_header1(TERSE, "Prepare butterfly computation");
507 
508  // Find parameter indices. We do this by the memory location of the
509  // model parameters.
510  GModelSky* skymodel = dynamic_cast<GModelSky*>(models[m_srcname]);
511  GModelPar* par_prefactor = &(*skymodel)["Prefactor"];
512  GModelPar* par_index = &(*skymodel)["Index"];
513  int inx_prefactor = -1;
514  int inx_index = -1;
515  for (int i = 0; i < models.npars(); ++i) {
516  if (models.pars()[i] == par_prefactor) {
517  inx_prefactor = i;
518  }
519  if (models.pars()[i] == par_index) {
520  inx_index = i;
521  }
522  }
523 
524  // Store model values
525  double prefactor_mean = (*skymodel)["Prefactor"].value();
526  double index_mean = (*skymodel)["Index"].value();
527  double pivot_mean = (*skymodel)["PivotEnergy"].value();
528  double prefactor_scale = (*skymodel)["Prefactor"].scale();
529  double index_scale = (*skymodel)["Index"].scale();
530  GEnergy pivot(pivot_mean, "MeV");
531 
532  // Write parameter indices into logger
533  log_value(NORMAL, "Prefactor", gammalib::str(prefactor_mean)+
534  " ph/cm2/s/MeV ("+
535  gammalib::str(inx_prefactor)+")");
536  log_value(NORMAL, "Index", gammalib::str(index_mean)+
537  " ("+gammalib::str(inx_index)+")");
538  log_value(NORMAL, "Pivot energy", pivot.print());
539 
540  // Extract covariance elements
541  double cov_pp = m_covariance(inx_prefactor,inx_prefactor);
542  double cov_pg = m_covariance(inx_prefactor,inx_index);
543  double cov_gg = m_covariance(inx_index,inx_index);
544 
545  // Compute eigenvectors and eigenvalues
546  double lambda1;
547  double lambda2;
548  GVector vector1(2);
549  GVector vector2(2);
550  eigenvectors(cov_pp, cov_pg, cov_pg, cov_gg,
551  &lambda1, &lambda2, &vector1, &vector2);
552 
553  // Write eigenvectors and eigenvalues into logger
554  log_value(NORMAL, "Eigenvalue 1", lambda1);
555  log_value(NORMAL, "Eigenvalue 2", lambda2);
556  log_value(NORMAL, "Eigenvector 1", vector1.print());
557  log_value(NORMAL, "Eigenvector 2", vector2.print());
558 
559  // Confidence scaling, calculated from a Chi-Squared distribution
560  // with 2 degrees of freedom
561  double scale = std::sqrt(-2.0 * std::log(1.0-m_confidence));
562 
563  // Write confidence level information into logger
564  log_value(NORMAL, "Confidence level", m_confidence);
565  log_value(NORMAL, "Corresponding scaling", scale);
566 
567  // Compute minor and major axes of error ellipse
568  double major = scale*std::sqrt(lambda1);
569  double minor = scale*std::sqrt(lambda2);
570 
571  // Write header into logger
572  log_header1(TERSE, "Generate butterfly");
573 
574  // Initialise result arrays
575  m_energies.assign(m_ebounds.size(), 0.0);
576  m_intensities.assign(m_ebounds.size(), 0.0);
577  m_min_intensities.assign(m_ebounds.size(), 1e30);
578  m_max_intensities.assign(m_ebounds.size(), 0.0);
579 
580  // Walk around the error ellipse
581  int nt = 360;
582  double dt = gammalib::twopi/double(nt);
583  double t = 0.0;
584  for (int i = 0; i < nt; ++i, t += dt) {
585 
586  // Pre-compute sin and cosine
587  double sin_t = std::sin(t);
588  double cos_t = std::cos(t);
589 
590  // Compute prefactor and index
591  double prefactor = (major * cos_t * vector1[0] +
592  minor * sin_t * vector2[0]) * prefactor_scale +
593  prefactor_mean;
594  double index = (major * cos_t * vector1[1] +
595  minor * sin_t * vector2[1]) * index_scale +
596  index_mean;
597 
598  // Write prefactor and index into logger
599  log_value(EXPLICIT, "Angle "+gammalib::str(t*gammalib::rad2deg),
600  gammalib::str(prefactor)+" ph/cm2/s/MeV; "+
601  gammalib::str(index));
602 
603  // Setup power law model
604  GModelSpectralPlaw plaw(prefactor, index, pivot);
605 
606  // Evaluate power law at each energy and extract minimum and
607  // maximum intensity
608  for (int k = 0; k < m_ebounds.size(); ++k) {
609 
610  // Get the energy of current bin
611  GTime time;
612  GEnergy energy = m_ebounds.elogmean(k);
613 
614  // Evaluate power law
615  double intensity = plaw.eval(energy, time);
616 
617  // Get extremes
618  if (intensity < m_min_intensities[k]) {
619  m_min_intensities[k] = intensity;
620  }
621  if (intensity > m_max_intensities[k]) {
622  m_max_intensities[k] = intensity;
623  }
624 
625  } // endfor: looped over all energies
626 
627  } // endfor: looped over all angles
628 
629  // Setup power law model for mean prefactor and index
630  GModelSpectralPlaw plaw(prefactor_mean, index_mean, pivot);
631 
632  // Compute energy and intensity vectors for mean power law
633  for (int k = 0; k < m_ebounds.size(); ++k) {
634 
635  // Get the energy of current bin
636  GTime time;
637  GEnergy energy = m_ebounds.elogmean(k);
638 
639  // Evaluate power law
640  double intensity = plaw.eval(energy, time);
641 
642  // Store results
643  m_energies[k] = energy.MeV();
644  m_intensities[k] = intensity;
645 
646  } // endfor: looped over energies
647 
648  // Return
649  return;
650 }
651 
652 
653 /***********************************************************************//**
654  * @brief Compute butterfly using Gaussian error propagation
655  *
656  * @param[in] models Models.
657  *
658  * Computes the butterfly diagram using Gaussian error propagation. This
659  * works for any kind of model.
660  ***************************************************************************/
662 {
663  // Write header into logger
664  log_header1(TERSE, "Prepare butterfly computation");
665 
666  // Confidence scaling
667  double scale = gammalib::erfinv(m_confidence) * gammalib::sqrt_two;
668 
669  // Write confidence level information into logger
670  log_value(NORMAL, "Confidence level", m_confidence);
671  log_value(NORMAL, "Corresponding scaling", scale);
672 
673  // Write header into logger
674  log_header1(TERSE, "Generate butterfly");
675 
676  // Initialise result arrays
677  m_energies.clear();
678  m_intensities.clear();
679  m_min_intensities.clear();
680  m_max_intensities.clear();
681 
682  // Initialise dummy time to evaluate spectral model
683  GTime time = GTime();
684 
685  // Initialise vector of gradients
686  GVector grad = GVector(m_obs.models().npars());
687 
688  // Loop over energy bins
689  for (int i = 0; i < m_ebounds.size(); ++i) {
690 
691  // Initialise number of gradients
692  int num_gradient = 0;
693 
694  // Get the mean logarithmic energy of current bin
695  GEnergy energy = m_ebounds.elogmean(i);
696 
697  // Initialise model flux value
698  double model_flux = 0.0;
699 
700  // Loop over models
701  for (int j = 0; j < models.size(); ++j) {
702 
703  // Check wether model is a skymodel
704  GModelSky* skymodel = dynamic_cast<GModelSky*>(models[j]);
705 
706  // Yes ...
707  if (skymodel != NULL) {
708 
709  // Set flux value of source of interest, evaluate gradients
710  if (skymodel->name() == m_srcname) {
711 
712  // Get spectral component of model
713  GModelSpectral *spectral = skymodel->spectral();
714 
715  // Get model flux
716  model_flux = spectral->eval(energy, time, true);
717 
718  // Loop over all model parameters
719  for (int k = 0; k < skymodel->size(); ++k) {
720 
721  // Set gradient if we deal with spectral component.
722  // Remember that the gradient() method returns full
723  // parameter gradients (including the scale factor)
724  bool parIsSpectral = false;
725  for (int l = 0; l < spectral->size(); ++l) {
726  if ((&((*spectral)[l])) == (&((*skymodel)[k]))) {
727  grad[num_gradient] = (*spectral)[l].gradient();
728  num_gradient++;
729  parIsSpectral = true;
730  break;
731  }
732  } // endfor: looped over spectral parameters
733 
734  // If parameter is spatial or temporal, count up
735  if (!parIsSpectral) {
736  num_gradient++;
737  }
738 
739  } // endfor: looped over all model parameters
740 
741  } // endif: model was source of interest
742  else {
743  num_gradient += skymodel->size();
744  }
745 
746  } // endif: model was sky model
747 
748  // Skip other models (e.g. GModelData instances)
749  else {
750  num_gradient += models[j]->size();
751  }
752 
753  } // endfor: Looped over models
754 
755  // Multiply covariance to the gradient vector
756  GVector vector = m_covariance * grad;
757 
758  // Get the error from the scalar product
759  double error = std::sqrt(grad * vector);
760 
761  // Multiply with confidence scaling
762  error *= scale;
763 
764  // Store flux, value and energy for saving
765  m_intensities.push_back(model_flux);
766  m_energies.push_back(energy.MeV());
767  m_min_intensities.push_back(model_flux-error);
768  m_max_intensities.push_back(model_flux+error);
769 
770  } // endfor: Looped over energy bins
771 
772  // Return
773  return;
774 }
775 
776 
777 /***********************************************************************//**
778  * @brief Check if sky model is valid
779  *
780  * @exception GException::invalid_value
781  * Did not find a valid model
782  *
783  * Checks whether the specified sky model exists and whether it is a power
784  * law.
785  ***************************************************************************/
787 {
788  // Continue only if source model exists
789  if (m_obs.models().contains(m_srcname)) {
790 
791  // Get relevant model and parameter for upper limit computation.
792  GModels& models = const_cast<GModels&>(m_obs.models());
793  GModelSky* model = dynamic_cast<GModelSky*>(models[m_srcname]);
794  if (model == NULL) {
795  std::string msg = "Source \""+m_srcname+"\" is not a sky model. "
796  "Please specify the name of a sky model for "
797  "butterfly computation.";
798  throw GException::invalid_value(G_CHECK_MODEL, msg);
799  }
800 
801  // Check that the spectral model is a power law for the "ENVELOPE"
802  // method
803  if (m_method == "ENVELOPE") {
804  if (model->spectral()->type() != "PowerLaw") {
805  std::string msg = "\""+model->spectral()->type()+"\" cannot be "
806  "used as spectral model for an butterfly "
807  "computation with method=ENVELOPE. Please "
808  "specify a power law model or switch to "
809  "method=GAUSSIAN.";
810  throw GException::invalid_value(G_CHECK_MODEL, msg);
811  }
812  }
813 
814  } // endif: source model existed
815 
816  // Otherwise throw an exception
817  else {
818  std::string msg = "Source \""+m_srcname+"\" not found in model "
819  "container. Please add a source with that name "
820  "or check for possible typos.";
821  throw GException::invalid_value(G_CHECK_MODEL, msg);
822  }
823 
824  // Return
825  return;
826 }
827 
828 
829 /***********************************************************************//**
830  * @brief Compute normalized eigenvectors and eigenvalues
831  *
832  * @param[in] a Covariance matrix element (0,0)
833  * @param[in] b Covariance matrix element (0,1)
834  * @param[in] c Covariance matrix element (1,0)
835  * @param[in] d Covariance matrix element (1,1)
836  * @param[out] lambda1 First eigenavlue
837  * @param[out] lambda2 Second eigenavlue
838  * @param[out] vector1 First eigenvector (normalized)
839  * @param[out] vector2 Second eigenvector (normalized)
840  *
841  * Computes the eigenvalues and eigenvectors for a 2x2 matrix. See
842  * http://www.math.harvard.edu/archive/21b_fall_04/exhibits/2dmatrices/index.html
843  ***************************************************************************/
844 void ctbutterfly::eigenvectors(const double& a,
845  const double& b,
846  const double& c,
847  const double& d,
848  double* lambda1,
849  double* lambda2,
850  GVector* vector1,
851  GVector* vector2)
852 {
853  // Compute trace and determinant
854  double trace = a + d;
855  double det = a*d - b*c;
856 
857  // Compute eigenvalues
858  double arg1 = 0.5*trace;
859  double arg2 = std::sqrt(0.25*trace*trace - det);
860  *lambda1 = arg1 + arg2;
861  *lambda2 = arg1 - arg2;
862 
863  // Compute eigenvectors
864  if (c != 0.0) {
865  (*vector1)[0] = *lambda1 - det;
866  (*vector1)[1] = c;
867  (*vector2)[0] = *lambda2 - det;
868  (*vector2)[1] = c;
869  }
870  else if (b != 0.0) {
871  (*vector1)[0] = b;
872  (*vector1)[1] = *lambda1 - a;
873  (*vector2)[0] = b;
874  (*vector2)[1] = *lambda2 - a;
875  }
876  else {
877  (*vector1)[0] = 1.0;
878  (*vector1)[1] = 0.0;
879  (*vector2)[0] = 0.0;
880  (*vector2)[1] = 1.0;
881  }
882 
883  // Normalize eigenvectors
884  *vector1 /= norm(*vector1);
885  *vector2 /= norm(*vector2);
886 
887  // Return
888  return;
889 }
890 
891 
892 /***********************************************************************//**
893  * @brief Set result FITS file
894  *
895  * Set the FITS file with the butterfly results.
896  ***************************************************************************/
898 {
899  // Determine the number of energies
900  int nrows = m_energies.size();
901 
902  // Allocate FITS table columns
903  GFitsTableDoubleCol col_energy("ENERGY", nrows);
904  GFitsTableDoubleCol col_intensity("INTENSITY", nrows);
905  GFitsTableDoubleCol col_intensity_min("INTENSITY_MIN", nrows);
906  GFitsTableDoubleCol col_intensity_max("INTENSITY_MAX", nrows);
907 
908  // Set units of columns
909  col_energy.unit("TeV");
910  col_intensity.unit("ph/cm2/s/TeV");
911  col_intensity_min.unit("ph/cm2/s/TeV");
912  col_intensity_max.unit("ph/cm2/s/TeV");
913 
914  // Fill columns and convert MeV units to TeV units
915  for (int i = 0; i < nrows; ++i) {
916  col_energy(i) = m_energies[i] * 1.0e-6;
917  col_intensity(i) = m_intensities[i] * 1.0e6;
918  col_intensity_min(i) = m_min_intensities[i] * 1.0e6;
919  col_intensity_max(i) = m_max_intensities[i] * 1.0e6;
920  }
921 
922  // Create binary table
923  GFitsBinTable table;
924  table.extname("BUTTERFLY");
925 
926  // Stamp header
927  stamp(table);
928 
929  // Add keywords
930  table.card("INSTRUME", "CTA", "Name of Instrument");
931  table.card("TELESCOP", "CTA", "Name of Telescope");
932 
933  // Append columns to table
934  table.append(col_energy);
935  table.append(col_intensity);
936  table.append(col_intensity_min);
937  table.append(col_intensity_max);
938 
939  // Create the FITS file
940  m_fits.clear();
941  m_fits.append(table);
942 
943  // Return
944  return;
945 }
ctbutterfly & operator=(const ctbutterfly &app)
Assignment operator.
void save(void)
Save butterfly diagram.
void setup_observations(GObservations &obs, const bool &response=true, const bool &list=true, const bool &cube=true)
Setup observation container.
Definition: ctool.cpp:431
#define G_CHECK_MODEL
Definition: ctbutterfly.cpp:39
void setup_models(GObservations &obs, const std::string &name="")
Setup model container.
Definition: ctool.cpp:545
GFits m_fits
FITS file holding butterfly.
ctlikelihood & operator=(const ctlikelihood &app)
Assignment operator.
void init_members(void)
Initialise class members.
Definition: ctool.cpp:321
const GObservations & obs(void) const
Return observation container.
GFilename m_outfile
Output ASCII file name.
void process(void)
Computes the butterfly.
ctbutterfly(void)
Void constructor.
Definition: ctbutterfly.cpp:55
void create_fits(void)
Set result FITS file.
double m_confidence
Confidence level.
Definition: ctbutterfly.hpp:96
std::vector< double > m_intensities
Power law intensity.
bool m_fit
Do fit?
Definition: ctbutterfly.hpp:99
#define CTBUTTERFLY_NAME
Definition: ctbutterfly.hpp:36
Butterfly calculation tool interface definition.
void set_obs_statistic(const std::string &statistic)
Set fit statistic for CTA observations.
void init_members(void)
Initialise class members.
void log_parameters(const GChatter &chatter)
Log application parameters.
Definition: ctool.cpp:1208
void clear(void)
Clear ctbutterfly tool.
GEbounds create_ebounds(void)
Create energy boundaries from user parameters.
Definition: ctool.cpp:612
void check_model(void)
Check if sky model is valid.
void ellipsoid_boundary(GModels &models)
Compute butterfly using the ellipsoid boundary method.
void free_members(void)
Delete class members.
bool m_apply_edisp
Apply energy dispersion?
Definition: ctbutterfly.hpp:98
void free_members(void)
Delete class members.
void eigenvectors(const double &a, const double &b, const double &c, const double &d, double *lambda1, double *lambda2, GVector *vector1, GVector *vector2)
Compute normalized eigenvectors and eigenvalues.
virtual ~ctbutterfly(void)
Destructor.
int m_max_iter
Maximum number of iterations.
Definition: ctbutterfly.hpp:97
void free_members(void)
Delete class members.
Definition: ctool.cpp:357
void init_members(void)
Initialise class members.
GChatter m_chatter
Chattiness.
void free_members(void)
Delete class members.
const bool & read_ahead(void) const
Signal whether parameters should be read ahead.
Definition: ctool.hpp:177
std::vector< double > m_min_intensities
Minimum intensities.
void gaussian_error_propagation(GModels &models)
Compute butterfly using Gaussian error propagation.
void init_members(void)
Initialise class members.
void copy_members(const ctbutterfly &app)
Copy class members.
void log_observations(const GChatter &chatter, const GObservations &obs, const std::string &what="Observation")
Log observation container.
Definition: ctool.cpp:1251
GObservations m_obs
Observation container.
std::vector< double > m_energies
Energy values.
GEbounds m_ebounds
Energy binning definition.
std::string m_method
Computation method.
Definition: ctbutterfly.hpp:95
std::string m_srcname
Name of source to compute butterfly.
Definition: ctbutterfly.hpp:94
Butterfly calculation tool.
Definition: ctbutterfly.hpp:55
Base class for likelihood tools.
std::vector< double > m_max_intensities
Maximum intensities.
GMatrixSparse m_covariance
Covariance matrix.
#define G_GET_PARAMETERS
Definition: ctbutterfly.cpp:37
void get_parameters(void)
Get application parameters.
GOptimizerLM m_opt
Optimizer.