Commit a92dd30446fc4b67bea53effb35ac6f8e24dbfd3

Authored by Jürgen Knödlseder
1 parent 5aa6adb3

Add "parname" parameter to ctulimit tool (#2233)

1 1 New Features and Important Changes in ctools 1.6.0
2 2  
3   -15 June 2018
  3 +20 June 2018
4 4  
5 5  
6 6 Introduction
... ... @@ -151,7 +151,9 @@ None
151 151  
152 152 ctulimit - Compute upper limit
153 153 ------------------------------
154   -None
  154 +Add "parname" parameter to allow upper limit computations also for a node
  155 +function spectral model. In addition, it is now checked that the parameter
  156 +values actually bracket the upper limit (#2233).
155 157  
156 158  
157 159 support - Support classes
... ...
src/ctulimit/ctulimit.cpp
... ... @@ -35,6 +35,8 @@
35 35  
36 36 /* __ Method name definitions ____________________________________________ */
37 37 #define G_GET_MODEL_PARAMETER "ctulimit::get_model_parameter()"
  38 +#define G_GET_PARAMETER_BRACKETS "ctulimit::get_parameter_brackets(double&, "\
  39 + "double&)"
38 40 #define G_UL_BISECTION "ctulimit::ul_bisection(double&, double&)"
39 41  
40 42 /* __ Debug definitions __________________________________________________ */
... ... @@ -266,24 +268,10 @@ void ctulimit::run(void)
266 268  
267 269 } // endif: likelihood was zero
268 270  
269   - // Extract current value and error
270   - double value = m_model_par->factor_value();
271   - double error = m_model_par->factor_error();
272   -
273   - // If parameter error is zero then take parameter value as error
274   - if (error == 0) {
275   - error = value;
276   - }
277   -
278   - // Compute parameter bracketing
279   - double parmin = value - m_sigma_min * error;
280   - double parmax = value + m_sigma_max * error;
281   - if (m_model_par->has_min() && m_model_par->factor_min() > parmin) {
282   - parmin = m_model_par->factor_min();
283   - }
284   - if (m_model_par->has_max() && m_model_par->factor_max() < parmax) {
285   - parmax = m_model_par->factor_max();
286   - }
  271 + // Get parameter brackets
  272 + double parmin;
  273 + double parmax;
  274 + get_parameter_brackets(parmin, parmax);
287 275  
288 276 // Write header into logger
289 277 log_header1(TERSE, "Compute upper limit");
... ... @@ -363,6 +351,7 @@ void ctulimit::init_members(void)
363 351 {
364 352 // Initialise user parameters
365 353 m_srcname.clear();
  354 + m_parname.clear();
366 355 m_confidence = 0.95;
367 356 m_sigma_min = 0.0;
368 357 m_sigma_max = 0.0;
... ... @@ -401,6 +390,7 @@ void ctulimit::copy_members(const ctulimit&amp; app)
401 390 {
402 391 // Copy user parameters
403 392 m_srcname = app.m_srcname;
  393 + m_parname = app.m_parname;
404 394 m_confidence = app.m_confidence;
405 395 m_sigma_min = app.m_sigma_min;
406 396 m_sigma_max = app.m_sigma_max;
... ... @@ -453,8 +443,9 @@ void ctulimit::get_parameters(void)
453 443 // Setup models from "inmodel" parameter
454 444 setup_models(m_obs, (*this)["srcname"].string());
455 445  
456   - // Get name of test source
  446 + // Get name of test source and optional parameter
457 447 m_srcname = (*this)["srcname"].string();
  448 + m_parname = (*this)["parname"].string();
458 449  
459 450 // Get relevant model and parameter for upper limit computation
460 451 get_model_parameter();
... ... @@ -532,22 +523,47 @@ void ctulimit::get_model_parameter(void)
532 523 "computation.";
533 524 throw GException::invalid_value(G_GET_MODEL_PARAMETER, msg);
534 525 }
535   - if (m_skymodel->spectral()->type() == "NodeFunction") {
536   - std::string msg = "\"NodeFunction\" cannot be used as spectral model "
537   - "for an upper limit computation. Please specify "
538   - "another spectral model.";
539   - throw GException::invalid_value(G_GET_MODEL_PARAMETER, msg);
540   - }
541 526  
542   - // Find appropriate model parameter and store its pointer in the
543   - // m_model_par member
544   - for (const char** par = pars; *par != NULL; ++par) {
545   - if (m_skymodel->spectral()->has_par(*par)) {
546   - m_model_par = &(m_skymodel->spectral()->operator[](*par));
547   - break;
  527 + // If a parameter name was specified then use that parameter
  528 + if (!m_parname.empty()) {
  529 + if (m_skymodel->spectral()->has_par(m_parname)) {
  530 + m_model_par = &(m_skymodel->spectral()->operator[](m_parname));
  531 + }
  532 + else {
  533 + std::string msg = "Spectral model of source \""+m_srcname+"\" has "
  534 + "no parameter \""+m_parname+"\". Please specify "
  535 + "a value parameter name of leave the parameter "
  536 + "name blank for autodetermination of an "
  537 + "intensity- or flux-like parameter.";
  538 + throw GException::invalid_value(G_GET_MODEL_PARAMETER, msg);
548 539 }
549 540 }
550 541  
  542 + // ... otherwise find the appropriate model parameter
  543 + else {
  544 +
  545 + // If a node function was specified then a valid parameter name is
  546 + // needed
  547 + if (m_skymodel->spectral()->type() == "NodeFunction") {
  548 + std::string msg = "Spectral model of source \""+m_srcname+"\" is "
  549 + "a \"NodeFunction\" but no parameter name was "
  550 + "specified. Please use the \"parname\" parameter "
  551 + "to specify the parameter that should be used "
  552 + "for the upper limit computation.";
  553 + throw GException::invalid_value(G_GET_MODEL_PARAMETER, msg);
  554 + }
  555 +
  556 + // Find appropriate model parameter and store its pointer in the
  557 + // m_model_par member
  558 + for (const char** par = pars; *par != NULL; ++par) {
  559 + if (m_skymodel->spectral()->has_par(*par)) {
  560 + m_model_par = &(m_skymodel->spectral()->operator[](*par));
  561 + break;
  562 + }
  563 + }
  564 +
  565 + } // endelse: searched for parameter
  566 +
551 567 // If no model parameter was found then throw an exception
552 568 if (m_model_par == NULL) {
553 569 std::string msg = "Require one of the following spectral "
... ... @@ -569,18 +585,105 @@ void ctulimit::get_model_parameter(void)
569 585  
570 586  
571 587 /***********************************************************************//**
  588 + * @brief Determine parameter brackets
  589 + *
  590 + * @param[out] parmin Minimum parameter value.
  591 + * @param[out] parmax Maximum parameter value.
  592 + *
  593 + * @exception GException::invalid_value
  594 + * Exceeded maximum number of iterations in the bracket search.
  595 + *
  596 + * Determines the brackets for the parameter of interest.
  597 + ***************************************************************************/
  598 +void ctulimit::get_parameter_brackets(double& parmin, double& parmax)
  599 +{
  600 + // Write header into logger
  601 + log_header1(TERSE, "Find parameter brackets");
  602 +
  603 + // Extract current value and error
  604 + double value = m_model_par->factor_value();
  605 + double error = m_model_par->factor_error();
  606 +
  607 + // If parameter error is zero then take parameter value as error
  608 + if (error == 0) {
  609 + error = value;
  610 + }
  611 +
  612 + // If parameter error is larger than parameter value then take parameter
  613 + // value as error
  614 + else if (std::abs(error) > std::abs(value)) {
  615 + error = value;
  616 + }
  617 +
  618 + // Compute parameter bracketing
  619 + parmin = value - m_sigma_min * error;
  620 + parmax = value + m_sigma_max * error;
  621 + if (m_model_par->has_min() && m_model_par->factor_min() > parmin) {
  622 + parmin = m_model_par->factor_min();
  623 + }
  624 + if (m_model_par->has_max() && m_model_par->factor_max() < parmax) {
  625 + parmax = m_model_par->factor_max();
  626 + }
  627 +
  628 + // Initialise iteration counter
  629 + int iter = 0;
  630 +
  631 + // Make sure that upper limit is bracketed
  632 + while (true) {
  633 +
  634 + // Throw exception if maximum iterations are reached
  635 + if (iter > m_max_iter) {
  636 + std::string msg = "The maximum number of "+gammalib::str(m_max_iter)+
  637 + " has been reached. You may consider to increase"
  638 + " the \"max_iter\" parameter and re-run ctulimit.";
  639 + throw GException::invalid_value(G_GET_PARAMETER_BRACKETS, msg);
  640 + }
  641 +
  642 + // Evaluate logL at upper boundary
  643 + double logL = evaluate(*m_model_par, parmax);
  644 + double eval_mid = logL - (m_best_logL + m_dlogL);
  645 +
  646 + // Write current parameter range into logger
  647 + log_value(NORMAL, "Parameter range",
  648 + "["+gammalib::str(parmin)+", "+gammalib::str(parmax)+"] "
  649 + "logL("+gammalib::str(parmax)+")="+gammalib::str(logL));
  650 +
  651 + // If the logL increase is not enough, increase maximum parameter
  652 + // value, otherwise break
  653 + if (eval_mid < 0.0) {
  654 + parmax *= 10.0;
  655 + }
  656 + else {
  657 + break;
  658 + }
  659 +
  660 + // Increase number of iterations
  661 + iter++;
  662 +
  663 + } // endwhile
  664 +
  665 + // Return
  666 + return;
  667 +}
  668 +
  669 +
  670 +/***********************************************************************//**
572 671 * @brief Calculate upper limit using a bisection method
573 672 *
574   - * @param[in] min Minimum parameter value
575   - * @param[in] max Maximum parameter value
  673 + * @param[in] parmin Minimum parameter value
  674 + * @param[in] parmax Maximum parameter value
  675 + *
  676 + * @exception GException::invalid_value
  677 + * Exceeded maximum number of iterations in the upper limit
  678 + * search.
576 679 *
577 680 * Calculates the upper limit using a bisection method.
578 681 ***************************************************************************/
579   -void ctulimit::ulimit_bisection(const double& min, const double& max)
  682 +void ctulimit::ulimit_bisection(const double& parmin, const double& parmax)
580 683 {
581 684 // Copy values to working values
582   - double wrk_min = min;
583   - double wrk_max = max;
  685 + double wrk_min = parmin;
  686 + double wrk_max = parmax;
584 687  
585 688 // Initialise iteration counter
586 689 int iter = 0;
... ... @@ -588,10 +691,6 @@ void ctulimit::ulimit_bisection(const double&amp; min, const double&amp; max)
588 691 // Loop until breaking condition is reached
589 692 while (true) {
590 693  
591   - // Log information
592   - log_value(EXPLICIT, "Iteration "+gammalib::str(iter),
593   - "["+gammalib::str(wrk_min)+", "+gammalib::str(wrk_max)+"]");
594   -
595 694 // Throw exception if maximum iterations are reached
596 695 if (iter > m_max_iter) {
597 696 std::string msg = "The maximum number of "+gammalib::str(m_max_iter)+
... ... @@ -604,15 +703,23 @@ void ctulimit::ulimit_bisection(const double&amp; min, const double&amp; max)
604 703 double mid = (wrk_min + wrk_max) / 2.0;
605 704  
606 705 // Calculate function value
607   - double eval_mid = evaluate(*m_model_par, mid) - (m_best_logL + m_dlogL);
  706 + double logL = evaluate(*m_model_par, mid);
  707 + double eval_mid = logL - (m_best_logL + m_dlogL);
  708 +
  709 + // Log information
  710 + log_value(EXPLICIT, "Iteration "+gammalib::str(iter),
  711 + "["+gammalib::str(wrk_min)+", "+gammalib::str(wrk_max)+"] "
  712 + "dlogL("+gammalib::str(mid)+")="+gammalib::str(eval_mid));
608 713  
609 714 // Check for convergence inside tolerance
610 715 if (std::abs(eval_mid) < m_tol) {
611 716 break;
612 717 }
  718 + /*
613 719 if (std::abs(wrk_max-wrk_min) < m_tol) {
614 720 break;
615 721 }
  722 + */
616 723  
617 724 // Change boundaries for further iteration
618 725 if (eval_mid > 0.0) {
... ...
src/ctulimit/ctulimit.hpp
... ... @@ -75,11 +75,13 @@ protected:
75 75 void free_members(void);
76 76 void get_parameters(void);
77 77 void get_model_parameter(void);
  78 + void get_parameter_brackets(double& parmin, double& parmax);
78 79 void ulimit_bisection(const double& parmin, const double& parmax);
79 80 void compute_ulimit(void);
80 81  
81 82 // User parameters
82   - std::string m_srcname; //!< Name of source which is moved around
  83 + std::string m_srcname; //!< Name of source for upper limit computation
  84 + std::string m_parname; //!< Name of parameter for upper limit computation
83 85 double m_confidence; //!< Confidence level
84 86 double m_sigma_min; //!< Starting value minimum (multiple fit errors above fit values)
85 87 double m_sigma_max; //!< Starting value maximum (multiple fit errors above fit values)
... ...
src/ctulimit/ctulimit.par
... ... @@ -19,6 +19,7 @@
19 19 inobs, f, a, events.fits,,, "Input event list, counts cube or observation definition XML file"
20 20 inmodel, f, a, $CTOOLS/share/models/crab.xml,,, "Input model definition XML file"
21 21 srcname, s, a, Crab,,, "Source of interest"
  22 +parname, s, h, ,,, "Parameter name (optional)"
22 23 expcube, f, a, NONE,,, "Input exposure cube file"
23 24 psfcube, f, a, NONE,,, "Input PSF cube file"
24 25 edispcube,f, a, NONE,,, "Input energy dispersion cube file"
... ...