ctools  2.0.0
 All Classes Namespaces Files Functions Variables Macros Pages
ctulimit.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * ctulimit - Upper limit calculation tool *
3  * ----------------------------------------------------------------------- *
4  * copyright (C) 2015-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 ctulimit.hpp
23  * @brief Upper limit calculation tool interface implementation
24  * @author Michael Mayer
25  */
26 
27 /* __ Includes ___________________________________________________________ */
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 #include <cstdio>
32 #include "ctulimit.hpp"
33 #include "GTools.hpp"
34 #include "GOptimizer.hpp"
35 
36 /* __ Method name definitions ____________________________________________ */
37 #define G_GET_MODEL_PARAMETER "ctulimit::get_model_parameter()"
38 #define G_GET_PARAMETER_BRACKETS "ctulimit::get_parameter_brackets(double&, "\
39  "double&)"
40 #define G_UL_BISECTION "ctulimit::ul_bisection(double&, double&)"
41 
42 /* __ Debug definitions __________________________________________________ */
43 
44 /* __ Coding definitions _________________________________________________ */
45 
46 
47 /*==========================================================================
48  = =
49  = Constructors/destructors =
50  = =
51  ==========================================================================*/
52 
53 /***********************************************************************//**
54  * @brief Void constructor
55  *
56  * Constructs an empty ctulimit tool.
57  ***************************************************************************/
59 {
60  // Initialise members
61  init_members();
62 
63  // Return
64  return;
65 }
66 
67 
68 /***********************************************************************//**
69  * @brief Observations constructor
70  *
71  * param[in] obs Observation container.
72  *
73  * Constructs ctulimit tool from an observations container.
74  ***************************************************************************/
75 ctulimit::ctulimit(const GObservations& obs) :
76  ctlikelihood(CTULIMIT_NAME, VERSION, obs)
77 {
78  // Initialise members
79  init_members();
80 
81  // Return
82  return;
83 }
84 
85 
86 
87 /***********************************************************************//**
88  * @brief Command line constructor
89  *
90  * @param[in] argc Number of arguments in command line.
91  * @param[in] argv Array of command line arguments.
92  *
93  * Constructs an instance of the ctulimit tool that will parse user
94  * parameters that are provided as command line arguments.
95  ***************************************************************************/
96 ctulimit::ctulimit(int argc, char *argv[]) :
97  ctlikelihood(CTULIMIT_NAME, VERSION, argc, argv)
98 {
99  // Initialise members
100  init_members();
101 
102  // Return
103  return;
104 }
105 
106 
107 /***********************************************************************//**
108  * @brief Copy constructor
109  *
110  * @param[in] app Application.
111  *
112  * Constructs an instance of the ctulimit tool by copying information from
113  * another ctulimit tool.
114  ***************************************************************************/
116 {
117  // Initialise members
118  init_members();
119 
120  // Copy members
121  copy_members(app);
122 
123  // Return
124  return;
125 }
126 
127 
128 /***********************************************************************//**
129  * @brief Destructor
130  *
131  * Destructs the ctulimit tool.
132  ***************************************************************************/
134 {
135  // Free members
136  free_members();
137 
138  // Return
139  return;
140 }
141 
142 
143 /*==========================================================================
144  = =
145  = Operators =
146  = =
147  ==========================================================================*/
148 
149 /***********************************************************************//**
150  * @brief Assignment operator
151  *
152  * @param[in] app Application.
153  * @return Application.
154  *
155  * Assigns a ctulimit tool.
156  ***************************************************************************/
158 {
159  // Execute only if object is not identical
160  if (this != &app) {
161 
162  // Copy base class members
163  this->ctlikelihood::operator=(app);
164 
165  // Free members
166  free_members();
167 
168  // Initialise members
169  init_members();
170 
171  // Copy members
172  copy_members(app);
173 
174  } // endif: object was not identical
175 
176  // Return this object
177  return *this;
178 }
179 
180 
181 /*==========================================================================
182  = =
183  = Public methods =
184  = =
185  ==========================================================================*/
186 
187 /***********************************************************************//**
188  * @brief Clear ctulimit tool
189  *
190  * Resets the ctulimit tool to a clean initial state.
191  ***************************************************************************/
192 void ctulimit::clear(void)
193 {
194  // Free members
195  free_members();
198  this->ctool::free_members();
199 
200  // Clear base class (needed to conserve tool name and version)
201  this->GApplication::clear();
202 
203  // Initialise members
204  this->ctool::init_members();
207  init_members();
208 
209  // Write header into logger
210  log_header();
211 
212  // Return
213  return;
214 }
215 
216 
217 /***********************************************************************//**
218  * @brief Compute upper limit
219  *
220  * Computes the upper limit depending on the given algorithm
221  ***************************************************************************/
223 {
224  // Get task parameters
225  get_parameters();
226 
227  // Set energy dispersion flags of all CTA observations and save old
228  // values in save_edisp vector
229  std::vector<bool> save_edisp = set_edisp(m_obs, m_apply_edisp);
230 
231  // Write input observation container into logger
232  log_observations(NORMAL, m_obs, "Input observation");
233 
234  // Save original models
235  GModels models_orig = m_obs.models();
236 
237  // Initialise best fit optimizer
238  GOptimizerLM best_opt;
239 
240  // Save original log-likelihood. If the value is zero it has never been
241  // computed hence we compute it now.
242  m_best_logL = m_obs.logL();
243  if (m_best_logL == 0.0) {
244 
245  // Write header into logger
246  log_header1(TERSE, "Compute best-fit likelihood");
247 
248  // Make sure that requested model parameter is free
249  m_model_par->free();
250 
251  // Optimize and save best log-likelihood
252  m_obs.optimize(m_opt);
253  m_obs.errors(m_opt);
254  m_best_logL = m_obs.logL();
255 
256  // Store optimizer for later recovery
257  best_opt = m_opt;
258 
259  // Write optimised model into logger
260  log_string(NORMAL, m_opt.print(m_chatter));
261  log_value(NORMAL,"Maximum log likelihood",gammalib::str(m_best_logL,3));
262  log_string(NORMAL, m_obs.models().print(m_chatter));
263 
264  } // endif: likelihood was zero
265 
266  // ... otherwise use original log-likelihood
267  else {
268 
269  // Write header into logger
270  log_header1(TERSE, "Extract best-fit likelihood");
271 
272  // Write optimised model into logger
273  log_value(NORMAL,"Maximum log likelihood",gammalib::str(m_best_logL,3));
274  log_string(NORMAL, m_obs.models().print(m_chatter));
275 
276  }
277 
278  // Get parameter brackets
279  double parmin;
280  double parmax;
281  get_parameter_brackets(parmin, parmax);
282 
283  // Write header into logger
284  log_header1(TERSE, "Compute upper limit");
285 
286  // Write some parameters into logger
287  log_value(NORMAL, "Model name", m_skymodel->name());
288  log_value(NORMAL, "Parameter name", m_model_par->name());
289  log_value(NORMAL, "Best factor value", m_best_value);
290  log_value(NORMAL, "Confidence level", gammalib::str(m_confidence*100.0)+"%");
291  log_value(NORMAL, "Maximum log likelihood",gammalib::str(m_best_logL,3));
292  log_value(NORMAL, "Log-likelihood difference", m_dlogL);
293  log_value(NORMAL, "Initial factor value range",
294  "["+gammalib::str(parmin)+", "+gammalib::str(parmax)+"]");
295 
296  // Compute upper limit
297  ulimit_bisection(parmin, parmax);
298 
299  // Write final parameter into logger
300  log_value(NORMAL, "Upper limit factor value", m_model_par->factor_value());
301  log_value(NORMAL, "Upper limit param. value", m_model_par->value());
302 
303  // Compute upper limit intensity and fluxes
304  compute_ulimit();
305 
306  // Write header into logger
307  log_header1(TERSE, "Upper limit results");
308 
309  // Write result into logger
310  log_value(TERSE, "Differential flux limit",
311  gammalib::str(m_diff_ulimit)+" ph/cm2/s/MeV at "+
312  gammalib::str(m_eref)+" TeV");
313  log_value(TERSE, "Integral flux limit",
314  gammalib::str(m_flux_ulimit)+" ph/cm2/s within ["+
315  gammalib::str(m_emin)+"-"+
316  gammalib::str(m_emax)+"] TeV");
317  log_value(TERSE, "Energy flux limit",
318  gammalib::str(m_eflux_ulimit)+" erg/cm2/s within ["+
319  gammalib::str(m_emin)+"-"+
320  gammalib::str(m_emax)+"] TeV");
321 
322  // Recover original models
323  m_obs.models(models_orig);
324 
325  // Recover optimizer
326  m_opt = best_opt;
327 
328  // Restore energy dispersion flags of all CTA observations
329  restore_edisp(m_obs, save_edisp);
330 
331  // Return
332  return;
333 }
334 
335 
336 /***********************************************************************//**
337  * @brief Save upper limits
338  *
339  * Saves the upper limit to an ASCII file in comma-separated value format.
340  *
341  * @todo No yet implemented as we have no clear use case yet for saving
342  * the result.
343  ***************************************************************************/
344 void ctulimit::save(void)
345 {
346  // Return
347  return;
348 }
349 
350 
351 /*==========================================================================
352  = =
353  = Private methods =
354  = =
355  ==========================================================================*/
356 
357 /***********************************************************************//**
358  * @brief Initialise class members
359  ***************************************************************************/
361 {
362  // Initialise user parameters
363  m_srcname.clear();
364  m_parname.clear();
365  m_confidence = 0.95;
366  m_sigma_min = 0.0;
367  m_sigma_max = 0.0;
368  m_eref = 0.0;
369  m_emin = 0.0;
370  m_emax = 0.0;
371  m_tol = 1.0e-6;
372  m_max_iter = 50;
373  m_apply_edisp = false;
374  m_chatter = static_cast<GChatter>(2);
375 
376  // Initialise protected members
377  m_dlogL = 0.0;
378  m_best_logL = 0.0;
379  m_best_value = 0.0;
380  m_best_error = 0.0;
381  m_flux_ulimit = 0.0;
382  m_diff_ulimit = 0.0;
383  m_eflux_ulimit = 0.0;
384  m_skymodel = NULL;
385  m_model_par = NULL;
386  m_is_spatial = false;
387 
388  // Return
389  return;
390 }
391 
392 
393 /***********************************************************************//**
394  * @brief Copy class members
395  *
396  * @param[in] app Application.
397  ***************************************************************************/
399 {
400  // Copy user parameters
401  m_srcname = app.m_srcname;
402  m_parname = app.m_parname;
404  m_sigma_min = app.m_sigma_min;
405  m_sigma_max = app.m_sigma_max;
406  m_eref = app.m_eref;
407  m_emin = app.m_emin;
408  m_emax = app.m_emax;
409  m_tol = app.m_tol;
410  m_max_iter = app.m_max_iter;
412  m_chatter = app.m_chatter;
413 
414  // Copy protected members
415  m_dlogL = app.m_dlogL;
416  m_best_logL = app.m_best_logL;
422 
423  // Extract model parameter
425 
426  // Return
427  return;
428 }
429 
430 
431 /***********************************************************************//**
432  * @brief Delete class members
433  ***************************************************************************/
435 {
436  // Return
437  return;
438 }
439 
440 
441 /***********************************************************************//**
442  * @brief Get application parameters
443  *
444  * Get all task parameters from parameter file.
445  ***************************************************************************/
447 {
448  // Setup observations from "inobs" parameter
450 
451  // Set observation statistic
452  set_obs_statistic(gammalib::toupper((*this)["statistic"].string()));
453 
454  // Setup models from "inmodel" parameter
455  setup_models(m_obs, (*this)["srcname"].string());
456 
457  // Get name of test source and optional parameter
458  m_srcname = (*this)["srcname"].string();
459  m_parname = (*this)["parname"].string();
460 
461  // Get relevant model and parameter for upper limit computation
463 
464  // Read energy dispersion flag
465  m_apply_edisp = (*this)["edisp"].boolean();
466 
467  // Get confidence level and transform into log-likelihood difference
468  m_confidence = (*this)["confidence"].real();
469  double sigma = gammalib::erfinv(m_confidence) * gammalib::sqrt_two;
470  m_dlogL = (sigma*sigma) / 2.0;
471 
472  // Read starting boundaries for bisection
473  m_sigma_min = (*this)["sigma_min"].real();
474  m_sigma_max = (*this)["sigma_max"].real();
475 
476  // Read energy values
477  m_eref = (*this)["eref"].real();
478  m_emin = (*this)["emin"].real();
479  m_emax = (*this)["emax"].real();
480 
481  // Set optimizer characteristics from user parameters
482  m_opt.eps((*this)["like_accuracy"].real());
483  m_opt.max_iter((*this)["max_iter"].integer());
484 
485  // Read precision
486  m_tol = (*this)["tol"].real();
487  m_max_iter = (*this)["max_iter"].integer();
488 
489  // Get remaining parameters
490  m_chatter = static_cast<GChatter>((*this)["chatter"].integer());
491 
492  // Write parameters into logger
493  log_parameters(TERSE);
494 
495  // Return
496  return;
497 }
498 
499 
500 /***********************************************************************//**
501  * @brief Get model parameter
502  *
503  * @exception GException::invalid_value
504  * Did not find a valid model or model parameter
505  *
506  * Extracts a pointer to the sky model (m_skymodel) and a pointer to the
507  * model parameter (m_model_par) that should be varied for the upper limit
508  * determination from the model container.
509  ***************************************************************************/
511 {
512  // Define list of valid spectral model parameters, terminated with a
513  // NULL pointer to signal the end of the list
514  const char* pars[] = {"Normalization", "Value", "Prefactor",
515  "Integral", "PhotonFlux", "EnergyFlux",
516  NULL};
517 
518  // Initialises sky model and model parameters pointers
519  m_skymodel = NULL;
520  m_model_par = NULL;
521  m_is_spatial = false;
522 
523  // If the model container does not container the specified source then
524  // throw an exception
525  if (!m_obs.models().contains(m_srcname)) {
526  std::string msg = "Source \""+m_srcname+"\" not found in model "
527  "container. Please add a source with that name "
528  "or check for possible typos.";
529  throw GException::invalid_value(G_GET_MODEL_PARAMETER, msg);
530  }
531 
532  // Get relevant sky model for upper limit computation. If the model is not
533  // a sky model or if the model has a "NodeFunction" as spectral component
534  // then throw an exception.
535  GModels& models = const_cast<GModels&>(m_obs.models());
536  m_skymodel = dynamic_cast<GModelSky*>(models[m_srcname]);
537  if (m_skymodel == NULL) {
538  std::string msg = "Source \""+m_srcname+"\" is not a sky model. Please "
539  "specify the name of a sky model for upper limit "
540  "computation.";
541  throw GException::invalid_value(G_GET_MODEL_PARAMETER, msg);
542  }
543 
544  // If a parameter name was specified then use that parameter
545  if (!m_parname.empty()) {
546  if (m_skymodel->spatial()->has_par(m_parname)) {
547  m_model_par = &(m_skymodel->spatial()->operator[](m_parname));
548  m_is_spatial = true;
549  }
550  else if (m_skymodel->spectral()->has_par(m_parname)) {
551  m_model_par = &(m_skymodel->spectral()->operator[](m_parname));
552  }
553  else {
554  std::string msg = "Spatial or spectral model of source \""+
555  m_srcname+"\" has no parameter \""+m_parname+
556  "\". Please specify a valid parameter name "
557  "or leave the parameter name blank for "
558  "autodetermination of an intensity- or "
559  "flux-like parameter.";
560  throw GException::invalid_value(G_GET_MODEL_PARAMETER, msg);
561  }
562  }
563 
564  // ... otherwise find the appropriate model parameter
565  else {
566 
567  // If a node function was specified then a valid parameter name is
568  // needed
569  if (m_skymodel->spectral()->type() == "NodeFunction") {
570  std::string msg = "Spectral model of source \""+m_srcname+"\" is "
571  "a \"NodeFunction\" but no parameter name was "
572  "specified. Please use the \"parname\" parameter "
573  "to specify the parameter that should be used "
574  "for the upper limit computation.";
575  throw GException::invalid_value(G_GET_MODEL_PARAMETER, msg);
576  }
577 
578  // Find appropriate model parameter and store its pointer in the
579  // m_model_par member
580  for (const char** par = pars; *par != NULL; ++par) {
581  if (m_skymodel->spectral()->has_par(*par)) {
582  m_model_par = &(m_skymodel->spectral()->operator[](*par));
583  break;
584  }
585  }
586 
587  } // endelse: searched for parameter
588 
589  // If no model parameter was found then throw an exception
590  if (m_model_par == NULL) {
591  std::string msg = "Require one of the following spectral "
592  "parameters for upper limit computation: ";
593  for (const char** par = pars; *par != NULL; ++par) {
594  if (par != pars) {
595  msg.append(", ");
596  }
597  msg.append("\""+std::string(*par)+"\"");
598  }
599  msg.append(". The specified source \""+m_srcname+"\" does not "
600  "have such a parameter.");
601  throw GException::invalid_value(G_GET_MODEL_PARAMETER, msg);
602  }
603 
604  // Return
605  return;
606 }
607 
608 
609 /***********************************************************************//**
610  * @brief Determine parameter brackets
611  *
612  * @param[out] parmin Minimum parameter value.
613  * @param[out] parmax Maximum parameter value.
614  *
615  * @exception GException::invalid_value
616  * Exceeded maximum number of iterations in the bracket search.
617  *
618  * Determines the brackets for the parameter of interest.
619  ***************************************************************************/
620 void ctulimit::get_parameter_brackets(double& parmin, double& parmax)
621 {
622  // Write header into logger
623  log_header1(TERSE, "Find parameter brackets");
624 
625  // Extract current value and error
626  m_best_value = m_model_par->factor_value();
627  m_best_error = m_model_par->factor_error();
628 
629  // If parameter error is zero then take parameter value as error
630  if (m_best_error == 0.0) {
632  }
633 
634  // Compute parameter bracketing
637  if (m_model_par->has_min() && m_model_par->factor_min() > parmin) {
638  parmin = m_model_par->factor_min();
639  }
640  if (m_model_par->has_max() && m_model_par->factor_max() < parmax) {
641  parmax = m_model_par->factor_max();
642  }
643 
644  // Initialise iteration counter
645  int iter = 0;
646 
647  // Make sure that upper limit is bracketed
648  while (true) {
649 
650  // Throw exception if maximum iterations are reached
651  if (iter > m_max_iter) {
652  std::string msg = "The maximum number of "+gammalib::str(m_max_iter)+
653  " has been reached. You may consider to increase"
654  " the \"max_iter\" parameter and re-run ctulimit.";
655  throw GException::invalid_value(G_GET_PARAMETER_BRACKETS, msg);
656  }
657 
658  // If model parameter is spatial parameter then make sure that no
659  // values are cached for source
660  if (m_is_spatial) {
661  m_obs.remove_response_cache(m_srcname);
662  }
663 
664  // Evaluate logL at upper boundary
665  double logL = evaluate(*m_model_par, parmax);
666  double eval_mid = logL - (m_best_logL + m_dlogL);
667 
668  // Write current parameter range into logger
669  log_value(NORMAL, "Parameter range",
670  "["+gammalib::str(parmin)+", "+gammalib::str(parmax)+"] "
671  "logL("+gammalib::str(parmax)+")="+gammalib::str(logL));
672 
673  // If the logL increase is not enough, increase maximum parameter
674  // value, otherwise break
675  if (eval_mid < 0.0) {
676  parmax *= 10.0;
677  }
678  else {
679  break;
680  }
681 
682  // Increase number of iterations
683  iter++;
684 
685  } // endwhile
686 
687  // Return
688  return;
689 }
690 
691 
692 /***********************************************************************//**
693  * @brief Calculate upper limit using a bisection method
694  *
695  * @param[in] parmin Minimum parameter value
696  * @param[in] parmax Maximum parameter value
697  *
698  * @exception GException::invalid_value
699  * Exceeded maximum number of iterations in the upper limit
700  * search.
701  *
702  * Calculates the upper limit using a bisection method. The bisection is
703  * stopped if the log-likelihood value is within the tolerance of the target
704  * value. The bisection is also stopped putting a warning in the log file if
705  * the parameter interval becomes smaller than a tolerance of 1.0e-6. The
706  * method stops with an exception if the maximum number of iterations is
707  * exhausted.
708  ***************************************************************************/
709 void ctulimit::ulimit_bisection(const double& parmin, const double& parmax)
710 {
711  // Copy values to working values
712  double wrk_min = parmin;
713  double wrk_max = parmax;
714 
715  // Initialise iteration counter and restart flag
716  int iter = 0;
717  bool restart = false;
718 
719  // Loop until breaking condition is reached
720  while (true) {
721 
722  // Throw exception if maximum iterations are reached
723  if (iter > m_max_iter) {
724  std::string msg = "The maximum number of "+gammalib::str(m_max_iter)+
725  " has been reached. You may consider to increase"
726  " the \"max_iter\" parameter and re-run ctulimit.";
727  throw GException::invalid_value(G_UL_BISECTION, msg);
728  }
729 
730  // If model parameter is spatial parameter then make sure that no
731  // values are cached for source
732  if (m_is_spatial) {
733  m_obs.remove_response_cache(m_srcname);
734  }
735 
736  // Compute center of boundary
737  double mid = (wrk_min + wrk_max) / 2.0;
738 
739  // Calculate function value
740  double logL = evaluate(*m_model_par, mid);
741  double eval_mid = logL - (m_best_logL + m_dlogL);
742 
743  // Log information
744  log_value(NORMAL, "Iteration "+gammalib::str(iter),
745  "["+gammalib::str(wrk_min)+", "+gammalib::str(wrk_max)+"] "
746  "logL("+gammalib::str(mid)+")="+gammalib::str(logL)+" "
747  "dlogL="+gammalib::str(eval_mid));
748 
749  // Check for convergence inside tolerance
750  if (std::abs(eval_mid) < m_tol) {
751  break;
752  }
753 
754  // Assess status
755  int status = 0;
756 
757  // Check whether mid-point is close to best parameter value
758  double eps = (mid != 0.0) ? (mid - m_best_value)/mid : mid - m_best_value;
759  if (std::abs(eps) < 1.0e-6) {
760  std::string msg =
761  " *** WARNING: Parameter range mid-point "+gammalib::str(mid)+" is "
762  "close to best factor\n"
763  " value "+gammalib::str(m_best_value)+" yet the "
764  "log-likelihood value "+gammalib::str(logL)+"\n"
765  " at the mid-point differs significantly from the "
766  "log-likelihood value\n"
767  " "+gammalib::str(m_best_logL)+" for the best factor "
768  "value.";
769  log_string(TERSE, msg);
770  status = 1;
771  }
772 
773  // Check for vanishing parameter range
774  eps = (mid != 0.0) ? (wrk_min - wrk_max)/mid : wrk_min - wrk_max;
775  if (std::abs(eps) < 1.0e-6) {
776  std::string msg =
777  " *** WARNING: Parameter range ["+gammalib::str(wrk_min)+", "+
778  gammalib::str(wrk_max)+"] has reduced\n"
779  " to a narrow interval without reaching convergence! "
780  "The best\n"
781  " log-likelihood value is probably not an absolute "
782  "but only a local\n"
783  " minimum.";
784  log_string(TERSE, msg);
785  status = 2;
786  }
787 
788  // Restart bisection if a restart is required
789  if (!restart && status != 0) {
790 
791  // Adopt current logL as best log-likelihood
792  restart = true;
793  iter = 0;
794  wrk_min = parmin;
795  wrk_max = parmax;
796  m_best_logL = logL;
797 
798  // Signal restart
799  std::string msg =
800  " Adopt "+gammalib::str(logL)+" as best log-likelihood and restart "
801  "bisection";
802  log_string(TERSE, msg);
803 
804  // Restart
805  continue;
806 
807  } // endif: restart bisection if required
808 
809  // Change boundaries for further iteration
810  if (eval_mid > 0.0) {
811  wrk_max = mid; // logL too large, new range = [wrk_min, mid]
812  }
813  else {
814  wrk_min = mid; // logL too small, new range = [mid, wrk_max]
815  }
816 
817  // Increment counter
818  iter++;
819 
820  } // endwhile
821 
822  // Return
823  return;
824 
825 }
826 
827 
828 /***********************************************************************//**
829  * @brief Compute upper limit intensity and fluxes
830  *
831  * Compute upper limit intensity and fluxes, including a correct computation
832  * for the diffuse map cube models.
833  ***************************************************************************/
835 {
836  // Initialise upper limit intensity and fluxes
837  m_diff_ulimit = 0.0;
838  m_flux_ulimit = 0.0;
839  m_eflux_ulimit = 0.0;
840 
841  // Get reference energy for differential upper limit
842  GEnergy eref = GEnergy(m_eref, "TeV");
843 
844  // Create energy range for flux limits
845  GEnergy emin = GEnergy(m_emin, "TeV");
846  GEnergy emax = GEnergy(m_emax, "TeV");
847 
848  // Set pointer to spectral model and initialise spectral nodes model
849  // pointer
850  GModelSpectral* spectral = m_skymodel->spectral();
851  GModelSpectralNodes* nodes = NULL;
852 
853  // If the spectral model is a diffuse cube then create a node function
854  // spectral model that is the product of the diffuse cube node function
855  // and the spectral model evaluated at the energies of the node function
856  GModelSpatialDiffuseCube* cube =
857  dynamic_cast<GModelSpatialDiffuseCube*>(m_skymodel->spatial());
858  if (cube != NULL) {
859 
860  // Set MC cone to the entire sky. This method call is needed to
861  // set-up the cube spectrum
862  cube->mc_cone(GSkyRegionCircle(0.0, 0.0, 180.0));
863 
864  // Allocate node function to replace the spectral component
865  nodes = new GModelSpectralNodes(cube->spectrum());
866  for (int i = 0; i < nodes->nodes(); ++i) {
867  GEnergy energy = nodes->energy(i);
868  double intensity = nodes->intensity(i);
869  double value = spectral->eval(energy);
870  nodes->intensity(i, value*intensity);
871  }
872 
873  // Set the spectral model pointer to the node function. If there are
874  // no nodes the spectral pointer is set to NULL since in this case the
875  // spectral->flux method will throw an exception
876  if (nodes->nodes() > 0) {
877  spectral = nodes;
878  }
879  else {
880  spectral = NULL;
881  }
882 
883  } // endif: spatial model was a diffuse cube
884 
885  // Compute upper limit intensity and fluxes
886  if (spectral != NULL) {
887  m_diff_ulimit = spectral->eval(eref);
888  m_flux_ulimit = spectral->flux(emin, emax);
889  m_eflux_ulimit = spectral->eflux(emin, emax);
890  }
891 
892  // Free spectral nodes if they have been allocated
893  if (nodes != NULL) {
894  delete nodes;
895  }
896 
897  // Return
898  return;
899 }
#define G_GET_PARAMETER_BRACKETS
Definition: ctulimit.cpp:38
Upper limit calculation tool interface implementation.
void setup_observations(GObservations &obs, const bool &response=true, const bool &list=true, const bool &cube=true)
Setup observation container.
Definition: ctool.cpp:431
ctulimit(void)
Void constructor.
Definition: ctulimit.cpp:58
#define G_UL_BISECTION
Definition: ctulimit.cpp:40
void setup_models(GObservations &obs, const std::string &name="")
Setup model container.
Definition: ctool.cpp:545
GChatter m_chatter
Chattiness.
Definition: ctulimit.hpp:94
ctlikelihood & operator=(const ctlikelihood &app)
Assignment operator.
void get_model_parameter(void)
Get model parameter.
Definition: ctulimit.cpp:510
double evaluate(GModelPar &par, const double &value)
Evaluates the log-likelihood function.
void init_members(void)
Initialise class members.
Definition: ctool.cpp:321
double m_dlogL
Likelihood difference for upper limit computation.
Definition: ctulimit.hpp:97
double m_diff_ulimit
Differential upper limit value.
Definition: ctulimit.hpp:101
Upper limit calculation tool.
Definition: ctulimit.hpp:50
void copy_members(const ctulimit &app)
Copy class members.
Definition: ctulimit.cpp:398
#define CTULIMIT_NAME
Definition: ctulimit.hpp:34
virtual ~ctulimit(void)
Destructor.
Definition: ctulimit.cpp:133
double m_emax
Maximum energy for flux limits (TeV)
Definition: ctulimit.hpp:90
void set_obs_statistic(const std::string &statistic)
Set fit statistic for CTA observations.
void free_members(void)
Delete class members.
Definition: ctulimit.cpp:434
double m_sigma_max
Starting value maximum (multiple fit errors above fit values)
Definition: ctulimit.hpp:87
double m_sigma_min
Starting value minimum (multiple fit errors above fit values)
Definition: ctulimit.hpp:86
GModelSky * m_skymodel
Pointer to sky model.
Definition: ctulimit.hpp:104
void log_parameters(const GChatter &chatter)
Log application parameters.
Definition: ctool.cpp:1208
std::string m_parname
Name of parameter for upper limit computation.
Definition: ctulimit.hpp:84
ctulimit & operator=(const ctulimit &app)
Assignment operator.
Definition: ctulimit.cpp:157
bool m_is_spatial
Signal that model parameter is spatial parameter.
Definition: ctulimit.hpp:106
void clear(void)
Clear ctulimit tool.
Definition: ctulimit.cpp:192
#define G_GET_MODEL_PARAMETER
Definition: ctulimit.cpp:37
void free_members(void)
Delete class members.
void free_members(void)
Delete class members.
double m_eflux_ulimit
Energy flux upper limits.
Definition: ctulimit.hpp:103
GModelPar * m_model_par
Pointer to model parameter.
Definition: ctulimit.hpp:105
void get_parameter_brackets(double &parmin, double &parmax)
Determine parameter brackets.
Definition: ctulimit.cpp:620
void free_members(void)
Delete class members.
Definition: ctool.cpp:357
double m_emin
Minimum energy for flux limits (TeV)
Definition: ctulimit.hpp:89
bool m_apply_edisp
Apply energy dispersion?
Definition: ctulimit.hpp:93
void init_members(void)
Initialise class members.
void get_parameters(void)
Get application parameters.
Definition: ctulimit.cpp:446
double m_confidence
Confidence level.
Definition: ctulimit.hpp:85
int m_max_iter
Maximum number of iterations.
Definition: ctulimit.hpp:92
void save(void)
Save upper limits.
Definition: ctulimit.cpp:344
void restore_edisp(GObservations &obs, const std::vector< bool > &edisp) const
Restore energy dispersion flags of CTA observations.
Definition: ctool.cpp:1689
void init_members(void)
Initialise class members.
Definition: ctulimit.cpp:360
std::vector< bool > set_edisp(GObservations &obs, const bool &edisp) const
Set energy dispersion to CTA observations.
Definition: ctool.cpp:1649
double m_best_value
Best parameter value factor.
Definition: ctulimit.hpp:99
void init_members(void)
Initialise class members.
double m_tol
Tolerance for limit determination.
Definition: ctulimit.hpp:91
void process(void)
Compute upper limit.
Definition: ctulimit.cpp:222
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.
double m_eref
Reference energy for flux limits (TeV)
Definition: ctulimit.hpp:88
double m_best_error
Best parameter value error.
Definition: ctulimit.hpp:100
Base class for likelihood tools.
void compute_ulimit(void)
Compute upper limit intensity and fluxes.
Definition: ctulimit.cpp:834
std::string m_srcname
Name of source for upper limit computation.
Definition: ctulimit.hpp:83
double m_flux_ulimit
Flux upper limit value.
Definition: ctulimit.hpp:102
GOptimizerLM m_opt
Optimizer.
double m_best_logL
Best fit log likelihood of given model.
Definition: ctulimit.hpp:98
void ulimit_bisection(const double &parmin, const double &parmax)
Calculate upper limit using a bisection method.
Definition: ctulimit.cpp:709