Commit a92dd30446fc4b67bea53effb35ac6f8e24dbfd3
1 parent
5aa6adb3
Add "parname" parameter to ctulimit tool (#2233)
Showing
4 changed files
with
156 additions
and
44 deletions
NEWS
| 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& 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& min, const double& 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& min, const double& 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" | ... | ... |