Commit a92dd30446fc4b67bea53effb35ac6f8e24dbfd3

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

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

1 New Features and Important Changes in ctools 1.6.0 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 Introduction 6 Introduction
@@ -151,7 +151,9 @@ None @@ -151,7 +151,9 @@ None
151 151
152 ctulimit - Compute upper limit 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 support - Support classes 159 support - Support classes
src/ctulimit/ctulimit.cpp
@@ -35,6 +35,8 @@ @@ -35,6 +35,8 @@
35 35
36 /* __ Method name definitions ____________________________________________ */ 36 /* __ Method name definitions ____________________________________________ */
37 #define G_GET_MODEL_PARAMETER "ctulimit::get_model_parameter()" 37 #define G_GET_MODEL_PARAMETER "ctulimit::get_model_parameter()"
  38 +#define G_GET_PARAMETER_BRACKETS "ctulimit::get_parameter_brackets(double&, "\
  39 + "double&)"
38 #define G_UL_BISECTION "ctulimit::ul_bisection(double&, double&)" 40 #define G_UL_BISECTION "ctulimit::ul_bisection(double&, double&)"
39 41
40 /* __ Debug definitions __________________________________________________ */ 42 /* __ Debug definitions __________________________________________________ */
@@ -266,24 +268,10 @@ void ctulimit::run(void) @@ -266,24 +268,10 @@ void ctulimit::run(void)
266 268
267 } // endif: likelihood was zero 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 // Write header into logger 276 // Write header into logger
289 log_header1(TERSE, "Compute upper limit"); 277 log_header1(TERSE, "Compute upper limit");
@@ -363,6 +351,7 @@ void ctulimit::init_members(void) @@ -363,6 +351,7 @@ void ctulimit::init_members(void)
363 { 351 {
364 // Initialise user parameters 352 // Initialise user parameters
365 m_srcname.clear(); 353 m_srcname.clear();
  354 + m_parname.clear();
366 m_confidence = 0.95; 355 m_confidence = 0.95;
367 m_sigma_min = 0.0; 356 m_sigma_min = 0.0;
368 m_sigma_max = 0.0; 357 m_sigma_max = 0.0;
@@ -401,6 +390,7 @@ void ctulimit::copy_members(const ctulimit&amp; app) @@ -401,6 +390,7 @@ void ctulimit::copy_members(const ctulimit&amp; app)
401 { 390 {
402 // Copy user parameters 391 // Copy user parameters
403 m_srcname = app.m_srcname; 392 m_srcname = app.m_srcname;
  393 + m_parname = app.m_parname;
404 m_confidence = app.m_confidence; 394 m_confidence = app.m_confidence;
405 m_sigma_min = app.m_sigma_min; 395 m_sigma_min = app.m_sigma_min;
406 m_sigma_max = app.m_sigma_max; 396 m_sigma_max = app.m_sigma_max;
@@ -453,8 +443,9 @@ void ctulimit::get_parameters(void) @@ -453,8 +443,9 @@ void ctulimit::get_parameters(void)
453 // Setup models from "inmodel" parameter 443 // Setup models from "inmodel" parameter
454 setup_models(m_obs, (*this)["srcname"].string()); 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 m_srcname = (*this)["srcname"].string(); 447 m_srcname = (*this)["srcname"].string();
  448 + m_parname = (*this)["parname"].string();
458 449
459 // Get relevant model and parameter for upper limit computation 450 // Get relevant model and parameter for upper limit computation
460 get_model_parameter(); 451 get_model_parameter();
@@ -532,22 +523,47 @@ void ctulimit::get_model_parameter(void) @@ -532,22 +523,47 @@ void ctulimit::get_model_parameter(void)
532 "computation."; 523 "computation.";
533 throw GException::invalid_value(G_GET_MODEL_PARAMETER, msg); 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 // If no model parameter was found then throw an exception 567 // If no model parameter was found then throw an exception
552 if (m_model_par == NULL) { 568 if (m_model_par == NULL) {
553 std::string msg = "Require one of the following spectral " 569 std::string msg = "Require one of the following spectral "
@@ -569,18 +585,105 @@ void ctulimit::get_model_parameter(void) @@ -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 * @brief Calculate upper limit using a bisection method 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 * Calculates the upper limit using a bisection method. 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 // Copy values to working values 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 // Initialise iteration counter 688 // Initialise iteration counter
586 int iter = 0; 689 int iter = 0;
@@ -588,10 +691,6 @@ void ctulimit::ulimit_bisection(const double&amp; min, const double&amp; max) @@ -588,10 +691,6 @@ void ctulimit::ulimit_bisection(const double&amp; min, const double&amp; max)
588 // Loop until breaking condition is reached 691 // Loop until breaking condition is reached
589 while (true) { 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 // Throw exception if maximum iterations are reached 694 // Throw exception if maximum iterations are reached
596 if (iter > m_max_iter) { 695 if (iter > m_max_iter) {
597 std::string msg = "The maximum number of "+gammalib::str(m_max_iter)+ 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,15 +703,23 @@ void ctulimit::ulimit_bisection(const double&amp; min, const double&amp; max)
604 double mid = (wrk_min + wrk_max) / 2.0; 703 double mid = (wrk_min + wrk_max) / 2.0;
605 704
606 // Calculate function value 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 // Check for convergence inside tolerance 714 // Check for convergence inside tolerance
610 if (std::abs(eval_mid) < m_tol) { 715 if (std::abs(eval_mid) < m_tol) {
611 break; 716 break;
612 } 717 }
  718 + /*
613 if (std::abs(wrk_max-wrk_min) < m_tol) { 719 if (std::abs(wrk_max-wrk_min) < m_tol) {
614 break; 720 break;
615 } 721 }
  722 + */
616 723
617 // Change boundaries for further iteration 724 // Change boundaries for further iteration
618 if (eval_mid > 0.0) { 725 if (eval_mid > 0.0) {
src/ctulimit/ctulimit.hpp
@@ -75,11 +75,13 @@ protected: @@ -75,11 +75,13 @@ protected:
75 void free_members(void); 75 void free_members(void);
76 void get_parameters(void); 76 void get_parameters(void);
77 void get_model_parameter(void); 77 void get_model_parameter(void);
  78 + void get_parameter_brackets(double& parmin, double& parmax);
78 void ulimit_bisection(const double& parmin, const double& parmax); 79 void ulimit_bisection(const double& parmin, const double& parmax);
79 void compute_ulimit(void); 80 void compute_ulimit(void);
80 81
81 // User parameters 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 double m_confidence; //!< Confidence level 85 double m_confidence; //!< Confidence level
84 double m_sigma_min; //!< Starting value minimum (multiple fit errors above fit values) 86 double m_sigma_min; //!< Starting value minimum (multiple fit errors above fit values)
85 double m_sigma_max; //!< Starting value maximum (multiple fit errors above fit values) 87 double m_sigma_max; //!< Starting value maximum (multiple fit errors above fit values)
src/ctulimit/ctulimit.par
@@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
19 inobs, f, a, events.fits,,, "Input event list, counts cube or observation definition XML file" 19 inobs, f, a, events.fits,,, "Input event list, counts cube or observation definition XML file"
20 inmodel, f, a, $CTOOLS/share/models/crab.xml,,, "Input model definition XML file" 20 inmodel, f, a, $CTOOLS/share/models/crab.xml,,, "Input model definition XML file"
21 srcname, s, a, Crab,,, "Source of interest" 21 srcname, s, a, Crab,,, "Source of interest"
  22 +parname, s, h, ,,, "Parameter name (optional)"
22 expcube, f, a, NONE,,, "Input exposure cube file" 23 expcube, f, a, NONE,,, "Input exposure cube file"
23 psfcube, f, a, NONE,,, "Input PSF cube file" 24 psfcube, f, a, NONE,,, "Input PSF cube file"
24 edispcube,f, a, NONE,,, "Input energy dispersion cube file" 25 edispcube,f, a, NONE,,, "Input energy dispersion cube file"