Commit 8ded0f5f0e46fecf704d04be8b839991b68dfbba

Authored by Florent Forest
1 parent 6577d6a2

cterror implementation

configure.ac
@@ -890,6 +890,7 @@ AC_CONFIG_FILES([Makefile @@ -890,6 +890,7 @@ AC_CONFIG_FILES([Makefile
890 src/cttsmap/Makefile 890 src/cttsmap/Makefile
891 src/ctbutterfly/Makefile 891 src/ctbutterfly/Makefile
892 src/ctulimit/Makefile 892 src/ctulimit/Makefile
  893 + src/cterror/Makefile
893 src/support/Makefile 894 src/support/Makefile
894 src/ctools-setup 895 src/ctools-setup
895 test/Makefile 896 test/Makefile
pyext/cterror.i 0 → 100644
  1 +/***************************************************************************
  2 + * cterror - Parameter error calculation tool *
  3 + * ----------------------------------------------------------------------- *
  4 + * copyright (C) 2015 by Florent Forest *
  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 cterror.i
  23 + * @brief Parameter error calculation tool interface definition
  24 + * @author Florent Forest
  25 + */
  26 +
  27 +%{
  28 +/* Put headers and other declarations here that are needed for compilation */
  29 +#include "cterror.hpp"
  30 +%}
  31 +
  32 +
  33 +/***********************************************************************//**
  34 + * @class cterror
  35 + *
  36 + * @brief Parameter error calculation tool
  37 + *
  38 + ***************************************************************************/
  39 +class cterror : public ctool {
  40 +
  41 + public:
  42 + // Constructors and destructors
  43 + cterror(void);
  44 + explicit cterror(const GObservations& obs);
  45 + cterror(int argc, char *argv[]);
  46 + cterror(const cterror& app);
  47 + virtual ~cterror(void);
  48 +
  49 + // Methods
  50 + void clear(void);
  51 + void run(void);
  52 + void save(void);
  53 + const GObservations& obs(void) const;
  54 + const GOptimizer* opt(void) const;
  55 +
  56 +};
  57 +
  58 +/***********************************************************************//**
  59 + * @brief Parameter error calculation tool Python extensions
  60 + ***************************************************************************/
  61 +%extend cterror {
  62 + cterror copy() {
  63 + return (*self);
  64 + }
  65 +}
pyext/ctools/obsutils.py
@@ -158,6 +158,37 @@ def fit(obs, log=False, debug=False, edisp=False): @@ -158,6 +158,37 @@ def fit(obs, log=False, debug=False, edisp=False):
158 # Return observations 158 # Return observations
159 return like 159 return like
160 160
  161 +# ================ #
  162 +# Run cterror #
  163 +# ================ #
  164 +def cterror(obs, log=False, debug=False):
  165 + """
  166 + Perform maximum likelihood fitting of observations in the container.
  167 +
  168 + Parameters:
  169 + obs - Observation container
  170 + Keywords:
  171 + log - Create log file(s)
  172 + debug - Create screen dump
  173 + edisp - Apply energy dispersion?
  174 + """
  175 + # Allocate cterror application
  176 + error = ctools.cterror(obs)
  177 +
  178 + # Optionally open the log file
  179 + if log:
  180 + error.logFileOpen()
  181 +
  182 + # Optionally switch-on debugging model
  183 + if debug:
  184 + error["debug"].boolean(True)
  185 +
  186 + # Run cterror application.
  187 + error.run()
  188 +
  189 + # Return observations
  190 + return error
  191 +
161 192
162 # ================= # 193 # ================= #
163 # Create counts map # 194 # Create counts map #
pyext/ctools/tools.i
@@ -59,3 +59,4 @@ @@ -59,3 +59,4 @@
59 %include "cttsmap.i" 59 %include "cttsmap.i"
60 %include "ctbutterfly.i" 60 %include "ctbutterfly.i"
61 %include "ctulimit.i" 61 %include "ctulimit.i"
  62 +%include "cterror.i"
scripts/cspull.py
@@ -297,8 +297,9 @@ class cspull(ctools.cscript): @@ -297,8 +297,9 @@ class cspull(ctools.cscript):
297 self.log("\n") 297 self.log("\n")
298 298
299 # Fit model 299 # Fit model
300 - like = obsutils.fit(obs, edisp=self.m_edisp, \  
301 - log=self.m_log, debug=self.m_debug) 300 + #like = obsutils.fit(obs, edisp=self.m_edisp, \
  301 + # log=self.m_log, debug=self.m_debug)
  302 + like = obsutils.cterror(obs, log=self.m_log, debug=self.m_debug)
302 303
303 # Store results 304 # Store results
304 logL = like.opt().value() 305 logL = like.opt().value()
src/Makefile.am
@@ -44,8 +44,8 @@ libctools_la_LIBADD = support/libsupport.la \ @@ -44,8 +44,8 @@ libctools_la_LIBADD = support/libsupport.la \
44 ctcubemask/libctcubemask.la \ 44 ctcubemask/libctcubemask.la \
45 cttsmap/libcttsmap.la \ 45 cttsmap/libcttsmap.la \
46 ctbutterfly/libctbutterfly.la \ 46 ctbutterfly/libctbutterfly.la \
47 - ctulimit/libctulimit.la  
48 - 47 + ctulimit/libctulimit.la \
  48 + cterror/libcterror.la
49 49
50 # Distribute scripts 50 # Distribute scripts
51 EXTRA_DIST = $(scripts) 51 EXTRA_DIST = $(scripts)
src/cterror/Makefile.am
@@ -9,10 +9,15 @@ bin_PROGRAMS = cterror @@ -9,10 +9,15 @@ bin_PROGRAMS = cterror
9 # Sources 9 # Sources
10 sources = cterror.cpp 10 sources = cterror.cpp
11 11
  12 +# Libtool library
  13 +noinst_LTLIBRARIES = libcterror.la
  14 +libcterror_la_SOURCES = $(sources)
  15 +
12 # Program definition 16 # Program definition
13 cterror_SOURCES = main.cpp 17 cterror_SOURCES = main.cpp
14 cterror_LDFLAGS = $(LIB_LINK_OPTION) @LDFLAGS@ 18 cterror_LDFLAGS = $(LIB_LINK_OPTION) @LDFLAGS@
15 cterror_CPPFLAGS = @CPPFLAGS@ 19 cterror_CPPFLAGS = @CPPFLAGS@
  20 +cterror_LDADD = libcterror.la $(top_srcdir)/src/support/libsupport.la
16 21
17 # Parameter files 22 # Parameter files
18 pfilesdir = $(prefix)/syspfiles 23 pfilesdir = $(prefix)/syspfiles
src/cterror/cterror.cpp
@@ -28,15 +28,16 @@ @@ -28,15 +28,16 @@
28 #ifdef HAVE_CONFIG_H 28 #ifdef HAVE_CONFIG_H
29 #include <config.h> 29 #include <config.h>
30 #endif 30 #endif
31 -#include <stdio> 31 +#include <iostream>
32 #include "cterror.hpp" 32 #include "cterror.hpp"
33 #include "GTools.hpp" 33 #include "GTools.hpp"
34 #include "GOptimizer.hpp" 34 #include "GOptimizer.hpp"
35 35
36 /* __ Method name definitions ____________________________________________ */ 36 /* __ Method name definitions ____________________________________________ */
37 #define G_GET_PARAMETERS "cterror::get_parameters()" 37 #define G_GET_PARAMETERS "cterror::get_parameters()"
38 -#define G_GET_MODEL_PARAMETER "cterror::get_model_parameter()" 38 +#define G_ERR_BISECTION "cterror::error_bisection(double&, double&)"
39 #define G_EVALUATE "cterror::evaluate(double&)" 39 #define G_EVALUATE "cterror::evaluate(double&)"
  40 +#define G_RUN "cterror::run()"
40 41
41 /* __ Debug definitions __________________________________________________ */ 42 /* __ Debug definitions __________________________________________________ */
42 43
@@ -230,92 +231,161 @@ void cterror::run(void) @@ -230,92 +231,161 @@ void cterror::run(void)
230 // Save original models 231 // Save original models
231 GModels models_orig = m_obs.models(); 232 GModels models_orig = m_obs.models();
232 233
233 - // Save original log-likelihood. If the value is zero it has never been  
234 - // computed hence we compute it now. 234 + // Allocate optimizer
  235 + m_opt = new GOptimizerLM();
  236 + // Optimize and save best log-likelihood
  237 + m_obs.optimize(*m_opt);
  238 + m_obs.errors(*m_opt);
235 m_best_logL = m_obs.logL(); 239 m_best_logL = m_obs.logL();
236 - if (m_best_logL == 0.0) {  
237 -  
238 - // Write header  
239 - if (logTerse()) {  
240 - log << std::endl;  
241 - log.header1("Compute best-fit likelihood");  
242 - }  
243 -  
244 - // Reoptimize if likelihood was not given before  
245 - GOptimizerLM* opt = new GOptimizerLM();  
246 - m_obs.optimize(*opt);  
247 - m_obs.errors(*opt);  
248 - m_best_logL = m_obs.logL();  
249 -  
250 - // Write optimised model into logger  
251 - if (logTerse()) {  
252 - log << m_obs.models() << std::endl;  
253 - }  
254 -  
255 - } // endif: likelihood was zero  
256 -  
257 - // Compute parameter bracketing  
258 - double value = m_model_par->value();  
259 - double error = m_model_par->error();  
260 - double parmin = value + m_sigma_min * error;  
261 - double parmax = value + m_sigma_max * error;  
262 240
263 // Write header 241 // Write header
264 if (logTerse()) { 242 if (logTerse()) {
265 log << std::endl; 243 log << std::endl;
266 - log.header1("Compute upper limit");  
267 - log << gammalib::parformat("Model name");  
268 - log << m_skymodel->name() << std::endl;  
269 - log << gammalib::parformat("Parameter name");  
270 - log << m_model_par->name() << std::endl;  
271 - log << gammalib::parformat("Confidence level");  
272 - log << m_confidence*100.0 << "%" << std::endl;  
273 - log << gammalib::parformat("Log-likelihood difference");  
274 - log << m_dlogL << std::endl;  
275 - log << gammalib::parformat("Initial parameter range");  
276 - log << "[";  
277 - log << parmin;  
278 - log << ", ";  
279 - log << parmax;  
280 - log << "]" << std::endl; 244 + log.header1("Compute best-fit likelihood");
281 } 245 }
282 246
283 - // Compute lower boundary  
284 - error_bisection(parmin, value);  
285 - double value_lo = m_model_par->value();  
286 - 247 + // Write optimised model into logger
287 if (logTerse()) { 248 if (logTerse()) {
288 - log << gammalib::parformat("Final lower parameter value");  
289 - log << value_lo << std::endl; 249 + log << m_obs.models() << std::endl;
290 } 250 }
291 251
292 - // Reset parameter to previous value  
293 - m_model_par->value(value);  
294 252
295 - // Compute upper boundary  
296 - error_bisection(value, parmax);  
297 - double value_hi = m_model_par->value(); 253 + // Continue only if source model exists
  254 + if (m_obs.models().contains(m_srcname)) {
  255 +
  256 + GModels& models = const_cast<GModels&>(m_obs.models());
  257 + m_skymodel = dynamic_cast<GModelSky*>(models[m_srcname]);
  258 + if (m_skymodel == NULL) {
  259 + std::string msg = "Source \""+m_srcname+"\" is not a sky model. "
  260 + "Please specify the name of a sky model for "
  261 + "parameter error computation.";
  262 + throw GException::invalid_value(G_RUN, msg);
  263 + }
  264 +
  265 + int npars = m_skymodel->spectral()->size();
  266 +
  267 +
  268 + for (int i = 0; i < npars; ++i)
  269 + {
  270 +
  271 + // Re-optimize
  272 + m_obs.optimize(*m_opt);
  273 +
  274 + if(m_skymodel->spectral()->at(i).is_fixed())
  275 + continue;
  276 +
  277 + m_model_par = &(m_skymodel->spectral()->at(i));
  278 +
  279 + // Compute parameter bracketing
  280 + m_value = m_model_par->factor_value();
  281 + double parmin = std::max(m_model_par->factor_min(),
  282 + m_value - 10*m_model_par->factor_error());
  283 + double parmax = std::min(m_model_par->factor_max(),
  284 + m_value + 10*m_model_par->factor_error());
  285 +
  286 + // Write header
  287 + if (logTerse()) {
  288 + log << std::endl;
  289 + log.header1("Compute parameter error");
  290 + log << gammalib::parformat("Model name");
  291 + log << m_skymodel->name() << std::endl;
  292 + log << gammalib::parformat("Parameter name");
  293 + log << m_model_par->name() << std::endl;
  294 + log << gammalib::parformat("Confidence level");
  295 + log << m_confidence*100.0 << "%" << std::endl;
  296 + log << gammalib::parformat("Log-likelihood difference");
  297 + log << m_dlogL << std::endl;
  298 + log << gammalib::parformat("Initial parameter range");
  299 + log << "[";
  300 + log << parmin;
  301 + log << ", ";
  302 + log << parmax;
  303 + log << "]" << std::endl;
  304 + }
  305 +
  306 + // Compute lower boundary
  307 + error_bisection(parmin, m_value);
  308 + double value_lo = m_model_par->factor_value();
  309 +
  310 + if (logTerse()) {
  311 + log << gammalib::parformat("Final lower parameter value");
  312 + log << value_lo << std::endl;
  313 + }
  314 +
  315 + // Reset parameter to original value
  316 + m_model_par->factor_value(m_value);
  317 +
  318 + // Compute upper boundary
  319 + error_bisection(m_value, parmax);
  320 + double value_hi = m_model_par->factor_value();
  321 +
  322 + if (logTerse()) {
  323 + log << gammalib::parformat("Final upper parameter");
  324 + log << value_hi << std::endl;
  325 + }
  326 +
  327 + // Reset parameter to original value
  328 + m_model_par->factor_value(m_value);
  329 +
  330 + // Compute errors
  331 + m_error = 0.5*(value_hi - value_lo);
  332 + double error_max = std::max(value_hi-m_value, m_value-value_lo);
  333 + double error_min = std::min(value_hi-m_value, m_value-value_lo);
  334 +
  335 +
  336 + // Write results to logfile
  337 + if (logTerse()) {
  338 + log << std::endl;
  339 + log.header1("Error results");
  340 + log << gammalib::parformat("Max error");
  341 + log << error_max;
  342 + log << gammalib::parformat("Min error");
  343 + log << error_min;
  344 + log << gammalib::parformat("Mean parameter error");
  345 + log << m_error;
  346 + log << " " << m_model_par->unit();
  347 + log << std::endl;
  348 + log << gammalib::parformat("Parameter scale");
  349 + log << m_model_par->scale();
  350 + log << std::endl;
  351 + }
  352 +
  353 + // Save error result
  354 + m_model_par->factor_error(m_error);
  355 +
  356 + }
298 357
299 - if (logTerse()) {  
300 - log << gammalib::parformat("Final upper parameter");  
301 - log << value_hi << std::endl;  
302 } 358 }
303 359
304 - // Compute error  
305 - double error = value_hi - value_lo;  
306 360
307 - // Write results to logfile 361 + // Return
  362 + return;
  363 +}
  364 +
  365 +/***********************************************************************//**
  366 + * @brief Save maps
  367 + *
  368 + * This method saves the error to an ascii file.
  369 + *
  370 + * @todo No yet implemented as we have no clear use case yet for saving
  371 + * the result.
  372 + ***************************************************************************/
  373 +void cterror::save(void)
  374 +{
  375 + // Write header
308 if (logTerse()) { 376 if (logTerse()) {
309 log << std::endl; 377 log << std::endl;
310 - log.header1("Error results");  
311 - log << gammalib::parformat("Parameter error");  
312 - log << error;  
313 - log << " " << m_model_par->unit();  
314 - log << std::endl; 378 + log.header1("Save results");
315 } 379 }
316 380
317 - // Recover original models  
318 - m_obs.models(models_orig); 381 +
  382 + // Get output filename
  383 + std::string outmodel = (*this)["outfile"].filename();
  384 +
  385 + // Write results out as XML model
  386 + if (gammalib::toupper(outmodel) != "NONE") {
  387 + m_obs.models().save(outmodel);
  388 + }
319 389
320 // Return 390 // Return
321 return; 391 return;
@@ -334,11 +404,13 @@ void cterror::init_members(void) @@ -334,11 +404,13 @@ void cterror::init_members(void)
334 { 404 {
335 // Initialise user parameters 405 // Initialise user parameters
336 m_srcname.clear(); 406 m_srcname.clear();
337 - m_confidence = 0.95; 407 + m_confidence = 0.68;
338 m_sigma_min = 0.0; 408 m_sigma_min = 0.0;
339 m_sigma_max = 0.0; 409 m_sigma_max = 0.0;
340 - m_tol = 1.0e-6; 410 + m_tol = 1.0e-3;
341 m_max_iter = 50; 411 m_max_iter = 50;
  412 + m_value = 0.0;
  413 + m_error = 0.0;
342 414
343 // Initialise protected members 415 // Initialise protected members
344 m_obs.clear(); 416 m_obs.clear();
@@ -367,16 +439,11 @@ void cterror::copy_members(const cterror&amp; app) @@ -367,16 +439,11 @@ void cterror::copy_members(const cterror&amp; app)
367 m_tol = app.m_tol; 439 m_tol = app.m_tol;
368 m_max_iter = app.m_max_iter; 440 m_max_iter = app.m_max_iter;
369 441
370 -  
371 // Copy protected members 442 // Copy protected members
372 m_obs = app.m_obs; 443 m_obs = app.m_obs;
373 m_dlogL = app.m_dlogL; 444 m_dlogL = app.m_dlogL;
374 m_best_logL = app.m_best_logL; 445 m_best_logL = app.m_best_logL;
375 446
376 -  
377 - // Extract model parameter  
378 - get_model_parameter();  
379 -  
380 // Return 447 // Return
381 return; 448 return;
382 } 449 }
@@ -416,7 +483,7 @@ void cterror::get_parameters(void) @@ -416,7 +483,7 @@ void cterror::get_parameters(void)
416 } // endif: there was no observation in the container 483 } // endif: there was no observation in the container
417 484
418 485
419 - // If there is are no models associated with the observations then 486 + // If there are no models associated with the observations then
420 // load now the model definition 487 // load now the model definition
421 if (m_obs.models().size() == 0) { 488 if (m_obs.models().size() == 0) {
422 489
@@ -437,11 +504,9 @@ void cterror::get_parameters(void) @@ -437,11 +504,9 @@ void cterror::get_parameters(void)
437 throw GException::invalid_value(G_GET_PARAMETERS, msg); 504 throw GException::invalid_value(G_GET_PARAMETERS, msg);
438 } 505 }
439 506
440 - // Get relevant model and parameter for upper limit computation  
441 - get_model_parameter();  
442 -  
443 // Get confidence level and transform into log-likelihood difference 507 // Get confidence level and transform into log-likelihood difference
444 m_confidence = (*this)["confidence"].real(); 508 m_confidence = (*this)["confidence"].real();
  509 +
445 double sigma = gammalib::erfinv(m_confidence) * gammalib::sqrt_two; 510 double sigma = gammalib::erfinv(m_confidence) * gammalib::sqrt_two;
446 m_dlogL = (sigma*sigma) / 2.0; 511 m_dlogL = (sigma*sigma) / 2.0;
447 512
@@ -449,70 +514,10 @@ void cterror::get_parameters(void) @@ -449,70 +514,10 @@ void cterror::get_parameters(void)
449 m_sigma_min = (*this)["sigma_min"].real(); 514 m_sigma_min = (*this)["sigma_min"].real();
450 m_sigma_max = (*this)["sigma_max"].real(); 515 m_sigma_max = (*this)["sigma_max"].real();
451 516
452 - // Read energy values  
453 - m_eref = (*this)["eref"].real();  
454 - m_emin = (*this)["emin"].real();  
455 - m_emax = (*this)["emax"].real();  
456 -  
457 // Read precision 517 // Read precision
458 m_tol = (*this)["tol"].real(); 518 m_tol = (*this)["tol"].real();
459 m_max_iter = (*this)["max_iter"].integer(); 519 m_max_iter = (*this)["max_iter"].integer();
460 520
461 - // Return  
462 - return;  
463 -}  
464 -  
465 -  
466 -/***********************************************************************//**  
467 - * @brief Get application parameters  
468 - *  
469 - * @exception GException::invalid_value  
470 - * Did not find a valid model parameter  
471 - *  
472 - * Extracts a pointer to the sky model (m_skymodel) and a pointer to the  
473 - * relevant model parameter (m_model_par) from the model container.  
474 - ***************************************************************************/  
475 -void cterror::get_model_parameter(void)  
476 -{  
477 - // Continue only if source model exists  
478 - if (m_obs.models().contains(m_srcname)) {  
479 -  
480 - // Get relevant model and parameter for upper limit computation.  
481 - GModels& models = const_cast<GModels&>(m_obs.models());  
482 - m_skymodel = dynamic_cast<GModelSky*>(models[m_srcname]);  
483 - if (m_skymodel == NULL) {  
484 - std::string msg = "Source \""+m_srcname+"\" is not a sky model. "  
485 - "Please specify the name of a sky model for "  
486 - "upper limit computation.";  
487 - throw GException::invalid_value(G_GET_MODEL_PARAMETER, msg);  
488 - }  
489 - if (m_skymodel->spectral()->type() == "NodeFunction") {  
490 - std::string msg = "\"NodeFunction\" cannot be used as spectral "  
491 - "model for an upper limit computation. "  
492 - "Please specify another spectral model.";  
493 - throw GException::invalid_value(G_GET_MODEL_PARAMETER, msg);  
494 - }  
495 - if (m_skymodel->spectral()->has_par("Normalization")) {  
496 - m_model_par = &(m_skymodel->spectral()->operator[]("Normalization"));  
497 - }  
498 - else if (m_skymodel->spectral()->has_par("Prefactor")) {  
499 - m_model_par = &(m_skymodel->spectral()->operator[]("Prefactor"));  
500 - }  
501 - else if (m_skymodel->spectral()->has_par("Integral")) {  
502 - m_model_par = &(m_skymodel->spectral()->operator[]("Integral"));  
503 - }  
504 - else if (m_skymodel->spectral()->has_par("Value")) {  
505 - m_model_par = &(m_skymodel->spectral()->operator[]("Value"));  
506 - }  
507 - else {  
508 - std::string msg = "Require spectral parameter \"Normalization\", "  
509 - "\"Prefactor\", \"Integral\" or \"Value\" for "  
510 - "upper limit computation. The specified source "  
511 - "\""+m_srcname+"\" does not have such a parameter.";  
512 - throw GException::invalid_value(G_GET_MODEL_PARAMETER, msg);  
513 - }  
514 -  
515 - } // endif: source model existed  
516 521
517 // Return 522 // Return
518 return; 523 return;
@@ -551,10 +556,35 @@ void cterror::error_bisection(const double&amp; min, const double&amp; max) @@ -551,10 +556,35 @@ void cterror::error_bisection(const double&amp; min, const double&amp; max)
551 556
552 // Throw exception if maximum iterations are reached 557 // Throw exception if maximum iterations are reached
553 if (iter > m_max_iter) { 558 if (iter > m_max_iter) {
554 - std::string msg = "The maximum number of "+gammalib::str(m_max_iter)+  
555 - " has been reached. You may consider to increase"  
556 - " the \"max_iter\" parameter and re-run cterror.";  
557 - throw GException::invalid_value(G_UL_BISECTION, msg); 559 + if(wrk_min - m_model_par->factor_min() < m_tol)
  560 + {
  561 + std::string msg = "The "+m_model_par->name()+" parameter minimum has been"
  562 + " reached during error calculation. To obtain accurate "
  563 + " errors, consider to set the minimum to a lower value,"
  564 + " and re-run cterror.";
  565 + if (logTerse()) {
  566 + log << msg;
  567 + }
  568 + break;
  569 + }
  570 + else if(m_model_par->factor_max() - wrk_max < m_tol)
  571 + {
  572 + std::string msg = "The "+m_model_par->name()+" parameter maximum has been"
  573 + " reached during error calculation. To obtain accurate "
  574 + " errors, consider to set the maxmimum to a higher value"
  575 + ", and re-run cterror.";
  576 + if (logTerse()) {
  577 + log << msg;
  578 + }
  579 + break;
  580 + }
  581 + else
  582 + {
  583 + std::string msg = "The maximum number of "+gammalib::str(m_max_iter)+
  584 + " has been reached. You may consider to increase"
  585 + " the \"max_iter\" parameter, and re-run cterror.";
  586 + throw GException::invalid_value(G_ERR_BISECTION, msg);
  587 + }
558 } 588 }
559 589
560 // Compute center of boundary 590 // Compute center of boundary
@@ -568,12 +598,27 @@ void cterror::error_bisection(const double&amp; min, const double&amp; max) @@ -568,12 +598,27 @@ void cterror::error_bisection(const double&amp; min, const double&amp; max)
568 break; 598 break;
569 } 599 }
570 600
571 - // Change boundaries for further iteration  
572 - if (eval_mid > 0.0) {  
573 - wrk_max = mid; 601 + // If we are on the crescent side of the parabola
  602 + if (mid > m_value)
  603 + {
  604 + // Change boundaries for further iteration
  605 + if (eval_mid > 0.0) {
  606 + wrk_max = mid;
  607 + }
  608 + else if (eval_mid < 0.0) {
  609 + wrk_min = mid;
  610 + }
574 } 611 }
575 - else if (eval_mid < 0.0) {  
576 - wrk_min = mid; 612 + // If we are on the decrescent side of the parabola
  613 + else
  614 + {
  615 + // Change boundaries for further iteration
  616 + if (eval_mid > 0.0) {
  617 + wrk_min = mid;
  618 + }
  619 + else if (eval_mid < 0.0) {
  620 + wrk_max = mid;
  621 + }
577 } 622 }
578 623
579 // Increment counter 624 // Increment counter
@@ -602,10 +647,16 @@ double cterror::evaluate(const double&amp; value) @@ -602,10 +647,16 @@ double cterror::evaluate(const double&amp; value)
602 double logL = 0.0; 647 double logL = 0.0;
603 648
604 // Check if given parameter is within boundaries 649 // Check if given parameter is within boundaries
605 - if (value > m_model_par->min() && value < m_model_par->max()) { 650 + if (value > m_model_par->factor_min() && value < m_model_par->factor_max()) {
606 651
607 // Change parameter factor 652 // Change parameter factor
608 - m_model_par->value(value); 653 + m_model_par->factor_value(value);
  654 +
  655 + //Fix parameter
  656 + m_model_par->fix();
  657 +
  658 + // Re-optimize
  659 + m_obs.optimize(*m_opt);
609 660
610 // Evaluate likelihood for new model container 661 // Evaluate likelihood for new model container
611 m_obs.eval(); 662 m_obs.eval();
@@ -613,6 +664,9 @@ double cterror::evaluate(const double&amp; value) @@ -613,6 +664,9 @@ double cterror::evaluate(const double&amp; value)
613 // Retrieve likelihood 664 // Retrieve likelihood
614 logL = m_obs.logL(); 665 logL = m_obs.logL();
615 666
  667 + // Free parameter
  668 + m_model_par->free();
  669 +
616 } // endif: value was inside allowed range 670 } // endif: value was inside allowed range
617 671
618 // ... otherwise signal that the parameter went outside the boundaries 672 // ... otherwise signal that the parameter went outside the boundaries
@@ -627,6 +681,7 @@ double cterror::evaluate(const double&amp; value) @@ -627,6 +681,7 @@ double cterror::evaluate(const double&amp; value)
627 // Compute function value 681 // Compute function value
628 double logL_difference = logL - m_best_logL - m_dlogL; 682 double logL_difference = logL - m_best_logL - m_dlogL;
629 683
  684 +
630 // Return 685 // Return
631 return logL_difference; 686 return logL_difference;
632 } 687 }
src/cterror/cterror.hpp
@@ -61,7 +61,7 @@ public: @@ -61,7 +61,7 @@ public:
61 void run(void); 61 void run(void);
62 void save(void); 62 void save(void);
63 const GObservations& obs(void) const; 63 const GObservations& obs(void) const;
64 - 64 + const GOptimizer* opt(void) const;
65 65
66 protected: 66 protected:
67 // Protected methods 67 // Protected methods
@@ -69,7 +69,6 @@ protected: @@ -69,7 +69,6 @@ protected:
69 void copy_members(const cterror& app); 69 void copy_members(const cterror& app);
70 void free_members(void); 70 void free_members(void);
71 void get_parameters(void); 71 void get_parameters(void);
72 - void get_model_parameter(void);  
73 double evaluate(const double& value); 72 double evaluate(const double& value);
74 void error_bisection(const double& min, const double& max); 73 void error_bisection(const double& min, const double& max);
75 74
@@ -80,6 +79,8 @@ protected: @@ -80,6 +79,8 @@ protected:
80 double m_sigma_max; //!< Starting value maximum (multiple fit errors above fit values) 79 double m_sigma_max; //!< Starting value maximum (multiple fit errors above fit values)
81 double m_tol; //!< Tolerance for limit determination 80 double m_tol; //!< Tolerance for limit determination
82 int m_max_iter; //!< Maximum number of iterations 81 int m_max_iter; //!< Maximum number of iterations
  82 + double m_value; //!< Parameter value
  83 + double m_error; //!< Parameter error
83 84
84 // Protected members 85 // Protected members
85 GObservations m_obs; //!< Observation container 86 GObservations m_obs; //!< Observation container
@@ -87,6 +88,7 @@ protected: @@ -87,6 +88,7 @@ protected:
87 GModelSky* m_skymodel; //!< Pointer to sky model 88 GModelSky* m_skymodel; //!< Pointer to sky model
88 GModelPar* m_model_par; //!< Pointer to model parameter 89 GModelPar* m_model_par; //!< Pointer to model parameter
89 double m_best_logL; //!< Best fit log likelihood of given model 90 double m_best_logL; //!< Best fit log likelihood of given model
  91 + GOptimizerLM* m_opt;
90 92
91 }; 93 };
92 94
@@ -102,5 +104,15 @@ const GObservations&amp; cterror::obs(void) const @@ -102,5 +104,15 @@ const GObservations&amp; cterror::obs(void) const
102 return m_obs; 104 return m_obs;
103 } 105 }
104 106
  107 +/***********************************************************************//**
  108 + * @brief Return optimizer
  109 + *
  110 + * @return Pointer to optimizer
  111 + ***************************************************************************/
  112 +inline
  113 +const GOptimizer* cterror::opt(void) const
  114 +{
  115 + return m_opt;
  116 +}
105 117
106 #endif /* CTERROR_HPP */ 118 #endif /* CTERROR_HPP */
src/cterror/cterror.par
@@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
19 #=================== 19 #===================
20 inobs, f, a, "events.fits",,, "Input event list, counts cube or observation definition file" 20 inobs, f, a, "events.fits",,, "Input event list, counts cube or observation definition file"
21 inmodel, f, a, "$CTOOLS/share/models/crab.xml",,, "Source model" 21 inmodel, f, a, "$CTOOLS/share/models/crab.xml",,, "Source model"
  22 +outfile, f, a, "results.xml",,, "Save results"
22 srcname, s, a, "Crab",,, "Source of interest" 23 srcname, s, a, "Crab",,, "Source of interest"
23 expcube, f, a, "NONE",,, "Exposure cube file (only needed for stacked analysis)" 24 expcube, f, a, "NONE",,, "Exposure cube file (only needed for stacked analysis)"
24 psfcube, f, a, "NONE",,, "PSF cube file (only needed for stacked analysis)" 25 psfcube, f, a, "NONE",,, "PSF cube file (only needed for stacked analysis)"
@@ -32,10 +33,7 @@ irf, s, a, &quot;cta_dummy_irf&quot;,,, &quot;Instrument response function&quot; @@ -32,10 +33,7 @@ irf, s, a, &quot;cta_dummy_irf&quot;,,, &quot;Instrument response function&quot;
32 confidence, r, h, 0.68,0.0,1.0, "Confidence level" 33 confidence, r, h, 0.68,0.0,1.0, "Confidence level"
33 sigma_min, r, h, 0.0,,, "Start minimum value (multiple sigmas above fit value)" 34 sigma_min, r, h, 0.0,,, "Start minimum value (multiple sigmas above fit value)"
34 sigma_max, r, h, 10.0,,, "Start maximum value (multiple sigmas above fit value)" 35 sigma_max, r, h, 10.0,,, "Start maximum value (multiple sigmas above fit value)"
35 -eref, r, h, 1.0,,, "Reference energy for differential limits (TeV)"  
36 -emin, r, h, 1.0,,, "Minimum energy for flux limits (TeV)"  
37 -emax, r, h, 100.0,,, "Maximum energy for flux limits (TeV)"  
38 -tol, r, h, 1e-5,,, "Computation tolerance" 36 +tol, r, h, 1e-3,,, "Computation tolerance"
39 max_iter, i, h, 50,,, "Maximum number of iterations" 37 max_iter, i, h, 50,,, "Maximum number of iterations"
40 38
41 # 39 #
src/cterror/main.cpp
@@ -28,7 +28,7 @@ @@ -28,7 +28,7 @@
28 #ifdef HAVE_CONFIG_H 28 #ifdef HAVE_CONFIG_H
29 #include <config.h> 29 #include <config.h>
30 #endif 30 #endif
31 -#include <cstdio> 31 +#include <iostream>
32 #include "cterror.hpp" 32 #include "cterror.hpp"
33 33
34 34