Commit a92dd30446fc4b67bea53effb35ac6f8e24dbfd3
1 parent
5aa6adb3
Add "parname" parameter to ctulimit tool (#2233)
Showing
4 changed files
with
156 additions
and
44 deletions
NEWS
| 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& app) | @@ -401,6 +390,7 @@ void ctulimit::copy_members(const ctulimit& 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& min, const double& max) | @@ -588,10 +691,6 @@ void ctulimit::ulimit_bisection(const double& min, const double& 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& min, const double& max) | @@ -604,15 +703,23 @@ void ctulimit::ulimit_bisection(const double& min, const double& 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" |