Commit 0d92b43b4e44fd91f2c02dbb060570802e13fbbc

Authored by Alexander Ziegler
1 parent 18b7c535

Started to impelement gaussian error propagation for ctbutterfly

src/ctbutterfly/ctbutterfly.cpp
@@ -243,12 +243,15 @@ void ctbutterfly::run(void) @@ -243,12 +243,15 @@ void ctbutterfly::run(void)
243 m_obs.optimize(m_opt); 243 m_obs.optimize(m_opt);
244 m_obs.errors(m_opt); 244 m_obs.errors(m_opt);
245 245
246 - // Get covariance matrix by inverting the curvature matrix. Note that  
247 - // the curvature matrix is for the unscaled parameters, hence we have  
248 - // to scale them before we can proceed. 246 + // Get covariance matrix.
249 GObservations::likelihood likelihood = m_obs.function(); 247 GObservations::likelihood likelihood = m_obs.function();
250 - m_covariance = likelihood.curvature()->invert();  
251 - 248 + if(!gepmode) {
  249 + m_covariance = likelihood.curvature()->invert();
  250 + }
  251 + else {
  252 + m_covariance = likelihood.covariance();
  253 + }
  254 +
252 // Write optimizer, optimised model and covariance matrix into logger 255 // Write optimizer, optimised model and covariance matrix into logger
253 log_string(NORMAL, m_opt.print(m_chatter)); 256 log_string(NORMAL, m_opt.print(m_chatter));
254 log_string(NORMAL, m_obs.models().print(m_chatter)); 257 log_string(NORMAL, m_obs.models().print(m_chatter));
@@ -271,10 +274,13 @@ void ctbutterfly::run(void) @@ -271,10 +274,13 @@ void ctbutterfly::run(void)
271 GObservations::likelihood likelihood = m_obs.function(); 274 GObservations::likelihood likelihood = m_obs.function();
272 likelihood.eval(pars); 275 likelihood.eval(pars);
273 276
274 - // Get covariance matrix by inverting the curvature matrix. Note that  
275 - // the curvature matrix is for the unscaled parameters, hence we have  
276 - // to scale them before we can proceed.  
277 - m_covariance = likelihood.curvature()->invert(); 277 + // Get covariance matrix.
  278 + if(!gepmode) {
  279 + m_covariance = likelihood.curvature()->invert();
  280 + }
  281 + else {
  282 + m_covariance = likelihood.covariance();
  283 + }
278 284
279 // Write covariance matrix into logger 285 // Write covariance matrix into logger
280 log_string(EXPLICIT, m_covariance.print(m_chatter)); 286 log_string(EXPLICIT, m_covariance.print(m_chatter));
@@ -284,105 +290,130 @@ void ctbutterfly::run(void) @@ -284,105 +290,130 @@ void ctbutterfly::run(void)
284 // Write header into logger 290 // Write header into logger
285 log_header1(TERSE, "Prepare butterfly computation"); 291 log_header1(TERSE, "Prepare butterfly computation");
286 292
287 - // Find parameter indices. We do this by the memory location of the  
288 - // model parameters.  
289 - GModelSky* skymodel = dynamic_cast<GModelSky*>(models[m_srcname]);  
290 - GModelPar* par_prefactor = &(*skymodel)["Prefactor"];  
291 - GModelPar* par_index = &(*skymodel)["Index"];  
292 - int inx_prefactor = -1;  
293 - int inx_index = -1;  
294 - for (int i = 0; i < models.npars(); ++i) {  
295 - if (models.pars()[i] == par_prefactor) {  
296 - inx_prefactor = i;  
297 - }  
298 - if (models.pars()[i] == par_index) {  
299 - inx_index = i; 293 + if (!m_gepmode) {
  294 +
  295 + // Find parameter indices. We do this by the memory location of the
  296 + // model parameters.
  297 + GModelSky* skymodel = dynamic_cast<GModelSky*>(models[m_srcname]);
  298 + GModelPar* par_prefactor = &(*skymodel)["Prefactor"];
  299 + GModelPar* par_index = &(*skymodel)["Index"];
  300 + int inx_prefactor = -1;
  301 + int inx_index = -1;
  302 + for (int i = 0; i < models.npars(); ++i) {
  303 + if (models.pars()[i] == par_prefactor) {
  304 + inx_prefactor = i;
  305 + }
  306 + if (models.pars()[i] == par_index) {
  307 + inx_index = i;
  308 + }
300 } 309 }
301 - }  
302 310
303 - // Store model values and scales  
304 - double prefactor_mean = (*skymodel)["Prefactor"].value();  
305 - double index_mean = (*skymodel)["Index"].value();  
306 - double pivot_mean = (*skymodel)["PivotEnergy"].value();  
307 - double prefactor_scale = (*skymodel)["Prefactor"].scale();  
308 - double index_scale = (*skymodel)["Index"].scale();  
309 - GEnergy pivot(pivot_mean, "MeV");  
310 -  
311 - // Write parameter indices into logger  
312 - log_value(NORMAL, "Prefactor", gammalib::str(prefactor_mean)+  
313 - " ph/cm2/s/MeV ("+gammalib::str(inx_prefactor)+")");  
314 - log_value(NORMAL, "Index", gammalib::str(index_mean)+  
315 - " ("+gammalib::str(inx_index)+")");  
316 - log_value(NORMAL, "Pivot energy", pivot.print());  
317 -  
318 - // Extract covariance elements  
319 - double cov_pp = m_covariance(inx_prefactor,inx_prefactor);  
320 - double cov_pg = m_covariance(inx_prefactor,inx_index);  
321 - double cov_gg = m_covariance(inx_index,inx_index);  
322 -  
323 - // Compute eigenvectors and eigenvalues  
324 - double lambda1;  
325 - double lambda2;  
326 - GVector vector1(2);  
327 - GVector vector2(2);  
328 - eigenvectors(cov_pp, cov_pg, cov_pg, cov_gg,  
329 - &lambda1, &lambda2, &vector1, &vector2);  
330 -  
331 - // Write eigenvectors and eigenvalues into logger  
332 - log_value(NORMAL, "Eigenvalue 1", lambda1);  
333 - log_value(NORMAL, "Eigenvalue 2", lambda2);  
334 - log_value(NORMAL, "Eigenvector 1", vector1.print());  
335 - log_value(NORMAL, "Eigenvector 2", vector2.print());  
336 -  
337 - // Confidence scaling  
338 - double sigma = gammalib::erfinv(m_confidence) * gammalib::sqrt_two;  
339 - double scale = (sigma*sigma);  
340 -  
341 - // Write confidence level information into logger  
342 - log_value(NORMAL, "Confidence level", m_confidence);  
343 - log_value(NORMAL, "Corresponding scaling", scale);  
344 -  
345 - // Compute minor and major axes of error ellipse  
346 - double major = scale*std::sqrt(lambda1);  
347 - double minor = scale*std::sqrt(lambda2); 311 + // Store model values
  312 + double prefactor_mean = (*skymodel)["Prefactor"].value();
  313 + double index_mean = (*skymodel)["Index"].value();
  314 + double pivot_mean = (*skymodel)["PivotEnergy"].value();
  315 + GEnergy pivot(pivot_mean, "MeV");
  316 +
  317 + // Write parameter indices into logger
  318 + log_value(NORMAL, "Prefactor", gammalib::str(prefactor_mean)+
  319 + " ph/cm2/s/MeV ("+gammalib::str(inx_prefactor)+")");
  320 + log_value(NORMAL, "Index", gammalib::str(index_mean)+
  321 + " ("+gammalib::str(inx_index)+")");
  322 + log_value(NORMAL, "Pivot energy", pivot.print());
  323 +
  324 + // Extract covariance elements
  325 + double cov_pp = m_covariance(inx_prefactor,inx_prefactor);
  326 + double cov_pg = m_covariance(inx_prefactor,inx_index);
  327 + double cov_gg = m_covariance(inx_index,inx_index);
  328 +
  329 + // Compute eigenvectors and eigenvalues
  330 + double lambda1;
  331 + double lambda2;
  332 + GVector vector1(2);
  333 + GVector vector2(2);
  334 + eigenvectors(cov_pp, cov_pg, cov_pg, cov_gg,
  335 + &lambda1, &lambda2, &vector1, &vector2);
  336 +
  337 + // Write eigenvectors and eigenvalues into logger
  338 + log_value(NORMAL, "Eigenvalue 1", lambda1);
  339 + log_value(NORMAL, "Eigenvalue 2", lambda2);
  340 + log_value(NORMAL, "Eigenvector 1", vector1.print());
  341 + log_value(NORMAL, "Eigenvector 2", vector2.print());
  342 +
  343 + // Confidence scaling
  344 + double sigma = gammalib::erfinv(m_confidence) * gammalib::sqrt_two;
  345 + double scale = (sigma*sigma);
  346 +
  347 + // Write confidence level information into logger
  348 + log_value(NORMAL, "Confidence level", m_confidence);
  349 + log_value(NORMAL, "Corresponding scaling", scale);
  350 +
  351 + // Compute minor and major axes of error ellipse
  352 + double major = scale*std::sqrt(lambda1);
  353 + double minor = scale*std::sqrt(lambda2);
348 354
349 - // Write header into logger  
350 - log_header1(TERSE, "Generate butterfly");  
351 -  
352 - // Initialise result arrays  
353 - m_energies.assign(m_ebounds.size(), 0.0);  
354 - m_intensities.assign(m_ebounds.size(), 0.0);  
355 - m_min_intensities.assign(m_ebounds.size(), 1e30);  
356 - m_max_intensities.assign(m_ebounds.size(), 0.0);  
357 -  
358 - // Walk around the error ellipse  
359 - int nt = 360;  
360 - double dt = gammalib::twopi/double(nt);  
361 - double t = 0.0;  
362 - for (int i = 0; i < nt; ++i, t += dt) {  
363 -  
364 - // Pre-compute sin and cosine  
365 - double sin_t = std::sin(t);  
366 - double cos_t = std::cos(t);  
367 -  
368 - // Compute prefactor and index  
369 - double prefactor = (major * cos_t * vector1[0] +  
370 - minor * sin_t * vector2[0]) * prefactor_scale +  
371 - prefactor_mean;  
372 - double index = (major * cos_t * vector1[1] +  
373 - minor * sin_t * vector2[1]) * index_scale +  
374 - index_mean;  
375 -  
376 - // Write prefactor and index into logger  
377 - log_value(EXPLICIT, "Angle "+gammalib::str(t*gammalib::rad2deg),  
378 - gammalib::str(prefactor)+" ph/cm2/s/MeV; "+  
379 - gammalib::str(index));  
380 -  
381 - // Setup power law model  
382 - GModelSpectralPlaw plaw(prefactor, index, pivot);  
383 -  
384 - // Evaluate power law at each energy and extract minimum and  
385 - // maximum intensity 355 + // Write header into logger
  356 + log_header1(TERSE, "Generate butterfly");
  357 +
  358 + // Initialise result arrays
  359 + m_energies.assign(m_ebounds.size(), 0.0);
  360 + m_intensities.assign(m_ebounds.size(), 0.0);
  361 + m_min_intensities.assign(m_ebounds.size(), 1e30);
  362 + m_max_intensities.assign(m_ebounds.size(), 0.0);
  363 +
  364 + // Walk around the error ellipse
  365 + int nt = 360;
  366 + double dt = gammalib::twopi/double(nt);
  367 + double t = 0.0;
  368 + for (int i = 0; i < nt; ++i, t += dt) {
  369 +
  370 + // Pre-compute sin and cosine
  371 + double sin_t = std::sin(t);
  372 + double cos_t = std::cos(t);
  373 +
  374 + // Compute prefactor and index
  375 + double prefactor = (major * cos_t * vector1[0] +
  376 + minor * sin_t * vector2[0]) +
  377 + prefactor_mean;
  378 + double index = (major * cos_t * vector1[1] +
  379 + minor * sin_t * vector2[1]) +
  380 + index_mean;
  381 +
  382 + // Write prefactor and index into logger
  383 + log_value(EXPLICIT, "Angle "+gammalib::str(t*gammalib::rad2deg),
  384 + gammalib::str(prefactor)+" ph/cm2/s/MeV; "+
  385 + gammalib::str(index));
  386 +
  387 + // Setup power law model
  388 + GModelSpectralPlaw plaw(prefactor, index, pivot);
  389 +
  390 + // Evaluate power law at each energy and extract minimum and
  391 + // maximum intensity
  392 + for (int k = 0; k < m_ebounds.size(); ++k) {
  393 +
  394 + // Get the energy of current bin
  395 + GTime time;
  396 + GEnergy energy = m_ebounds.elogmean(k);
  397 +
  398 + // Evaluate power law
  399 + double intensity = plaw.eval(energy, time);
  400 +
  401 + // Get extremes
  402 + if (intensity < m_min_intensities[k]) {
  403 + m_min_intensities[k] = intensity;
  404 + }
  405 + if (intensity > m_max_intensities[k]) {
  406 + m_max_intensities[k] = intensity;
  407 + }
  408 +
  409 + } // endfor: looped over all energies
  410 +
  411 + } // endfor: looped over all angles
  412 +
  413 + // Setup power law model for mean prefactor and index
  414 + GModelSpectralPlaw plaw(prefactor_mean, index_mean, pivot);
  415 +
  416 + // Compute energy and intensity vectors for mean power law
386 for (int k = 0; k < m_ebounds.size(); ++k) { 417 for (int k = 0; k < m_ebounds.size(); ++k) {
387 418
388 // Get the energy of current bin 419 // Get the energy of current bin
@@ -392,37 +423,112 @@ void ctbutterfly::run(void) @@ -392,37 +423,112 @@ void ctbutterfly::run(void)
392 // Evaluate power law 423 // Evaluate power law
393 double intensity = plaw.eval(energy, time); 424 double intensity = plaw.eval(energy, time);
394 425
395 - // Get extremes  
396 - if (intensity < m_min_intensities[k]) {  
397 - m_min_intensities[k] = intensity;  
398 - }  
399 - if (intensity > m_max_intensities[k]) {  
400 - m_max_intensities[k] = intensity;  
401 - } 426 + // Store results
  427 + m_energies[k] = energy.MeV();
  428 + m_intensities[k] = intensity;
  429 +
  430 + } // endfor: looped over energies
402 431
403 - } // endfor: looped over all energies 432 + } //endif: use butterfly envelope for computation
404 433
405 - } // endfor: looped over all angles 434 + else {
406 435
407 - // Setup power law model for mean prefactor and index  
408 - GModelSpectralPlaw plaw(prefactor_mean, index_mean, pivot); 436 + // Use gaussian error propagation scheme to calculate
  437 + // butterfly
  438 + log_header2(TERSE, "Using gaussian error propagation to"
  439 + " calculate butterfly");
409 440
410 - // Compute energy and intensity vectors for mean power law  
411 - for (int k = 0; k < m_ebounds.size(); ++k) { 441 + // Initialise dummy time to evaluate spectral model
  442 + GTime time = GTime();
412 443
413 - // Get the energy of current bin  
414 - GTime time;  
415 - GEnergy energy = m_ebounds.elogmean(k); 444 + // Initialise vector of gradients
  445 + GVector grad = GVector(m_obs.models().npars());
416 446
417 - // Evaluate power law  
418 - double intensity = plaw.eval(energy, time); 447 + // Loop over energy bins positions
  448 + for (int i = 0; i < m_ebounds.size(); ++i) {
419 449
420 - // Store results  
421 - m_energies[k] = energy.MeV();  
422 - m_intensities[k] = intensity; 450 + // Initialise number of gradients
  451 + int num_gradient = 0;
423 452
424 - } // endfor: looped over energies 453 + // Get the energy of current bin
  454 + GEnergy energy = m_ebounds.elogmean(i);
  455 +
  456 + // Log status info
  457 + std::string msg = "Computing butterfly for bin number "+
  458 + gammalib::str(i)+" at "+energy.print();
  459 + log_string(NORMAL, msg);
  460 +
  461 + // Initialise model flux value
  462 + double model_flux = 0.0;
  463 +
  464 + // Loop over models
  465 + for (int j = 0; j < models.size(); ++j) {
  466 +
  467 + // Check wether model is a skymodel
  468 + GModelSky* skymodel = dynamic_cast<GModelSky*>(models[j]);
  469 +
  470 + // Yes ...
  471 + if (skymodel != NULL) {
  472 +
  473 + // Skip spatial models
  474 + num_gradient += skymodel->spatial()->size();
  475 +
  476 + // Get pointer to spectral model
  477 + GModelSpectral* spectral = skymodel->spectral();
  478 +
  479 + // Set flux value of source of interest, evaluate gradients
  480 + if (skymodel->name() == m_srcname) {
  481 + model_flux = spectral->eval(energy, time, true);
  482 + } // endif: model was source of interest
  483 +
  484 + // Loop over model parameters, get gradients
  485 + // and assign them to the vector
  486 + if(skymodel->name() == m_srcname) {
  487 +
  488 + // set gradient if we deal with source of interest
  489 + for (int k = 0; k < spectral->size(); ++k) {
  490 + grad[num_gradient] = (*spectral)[k].gradient();
  491 + num_gradient++;
  492 +
  493 + } // endfor: looped over spectral parameters
  494 +
  495 + } //endif: spectrum was of source of interest
  496 + else{
  497 + // Skip gradient of other models (0=default)
  498 + num_gradient += skymodel->spectral()->size();
  499 +
  500 + } //endelse: Model was not model of interest
  501 +
  502 + // Skip temporal models
  503 + num_gradient += skymodel->temporal()->size();
  504 +
  505 + } // endif: model was sky model
  506 +
  507 + else {
  508 + // Skip other models (e.g. GModelData instances)
  509 + num_gradient += models[j]->size();
  510 +
  511 + } // endelse: Model was not GModelSky
  512 +
  513 + } // endfor: Looped over models
  514 +
  515 + // Multiply covariance to the gradient vector
  516 + GVector vector = m_covariance * grad;
  517 +
  518 + // Get the error from the scalar product
  519 + double error = std::sqrt(grad * vector);
  520 +
  521 + // Store flux, value and energy for saving
  522 + m_intensities.push_back(model_flux);
  523 + m_energies.push_back(energy.MeV());
  524 + m_min_intensities.push_back(model_flux-error);
  525 + m_max_intensities.push_back(model_flux+error);
  526 +
  527 + } //endfor: loop over energy bin
  528 +
  529 + } //endelse: use gaussian error propagation
425 530
  531 +
426 // Write header into logger 532 // Write header into logger
427 log_header1(TERSE, "Results"); 533 log_header1(TERSE, "Results");
428 534
@@ -434,9 +540,11 @@ void ctbutterfly::run(void) @@ -434,9 +540,11 @@ void ctbutterfly::run(void)
434 gammalib::str(m_intensities[k])+" ("+ 540 gammalib::str(m_intensities[k])+" ("+
435 gammalib::str(m_min_intensities[k])+", "+ 541 gammalib::str(m_min_intensities[k])+", "+
436 gammalib::str(m_max_intensities[k])+") ph/cm2/s/MeV"); 542 gammalib::str(m_max_intensities[k])+") ph/cm2/s/MeV");
  543 +
  544 + } //endfor: loop over energy bin
  545 +
437 546
438 - } // endfor: looped over energies  
439 - 547 +
440 // Restore energy dispersion flag for all CTA observations 548 // Restore energy dispersion flag for all CTA observations
441 for (int i = 0; i < m_obs.size(); ++i) { 549 for (int i = 0; i < m_obs.size(); ++i) {
442 GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]); 550 GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
@@ -507,6 +615,7 @@ void ctbutterfly::init_members(void) @@ -507,6 +615,7 @@ void ctbutterfly::init_members(void)
507 m_confidence = 0.68; 615 m_confidence = 0.68;
508 m_max_iter = 50; 616 m_max_iter = 50;
509 m_apply_edisp = false; 617 m_apply_edisp = false;
  618 + m_gepmode = false;
510 m_fit = false; 619 m_fit = false;
511 m_chatter = static_cast<GChatter>(2); 620 m_chatter = static_cast<GChatter>(2);
512 m_ebounds.clear(); 621 m_ebounds.clear();
@@ -540,6 +649,7 @@ void ctbutterfly::copy_members(const ctbutterfly&amp; app) @@ -540,6 +649,7 @@ void ctbutterfly::copy_members(const ctbutterfly&amp; app)
540 m_confidence = app.m_confidence; 649 m_confidence = app.m_confidence;
541 m_max_iter = app.m_max_iter; 650 m_max_iter = app.m_max_iter;
542 m_apply_edisp = app.m_apply_edisp; 651 m_apply_edisp = app.m_apply_edisp;
  652 + m_gepmode = app.m_gepmode;
543 m_fit = app.m_fit; 653 m_fit = app.m_fit;
544 m_chatter = app.m_chatter; 654 m_chatter = app.m_chatter;
545 m_ebounds = app.m_ebounds; 655 m_ebounds = app.m_ebounds;
@@ -590,9 +700,6 @@ void ctbutterfly::get_parameters(void) @@ -590,9 +700,6 @@ void ctbutterfly::get_parameters(void)
590 // Get name of test source 700 // Get name of test source
591 m_srcname = (*this)["srcname"].string(); 701 m_srcname = (*this)["srcname"].string();
592 702
593 - // Check model name and type  
594 - check_model();  
595 -  
596 // Create energy boundaries from user parameters 703 // Create energy boundaries from user parameters
597 m_ebounds = create_ebounds(); 704 m_ebounds = create_ebounds();
598 705
@@ -608,10 +715,16 @@ void ctbutterfly::get_parameters(void) @@ -608,10 +715,16 @@ void ctbutterfly::get_parameters(void)
608 715
609 // Get remaining parameters 716 // Get remaining parameters
610 m_apply_edisp = (*this)["edisp"].boolean(); 717 m_apply_edisp = (*this)["edisp"].boolean();
  718 + m_gepmode = (*this)["gepmode"].boolean();
611 m_confidence = (*this)["confidence"].real(); 719 m_confidence = (*this)["confidence"].real();
612 m_fit = (*this)["fit"].boolean(); 720 m_fit = (*this)["fit"].boolean();
613 m_chatter = static_cast<GChatter>((*this)["chatter"].integer()); 721 m_chatter = static_cast<GChatter>((*this)["chatter"].integer());
614 722
  723 + if (!m_gepmode) {
  724 + // Check model name and type
  725 + check_model();
  726 + }
  727 +
615 // Optionally read ahead parameters so that they get correctly 728 // Optionally read ahead parameters so that they get correctly
616 // dumped into the log file 729 // dumped into the log file
617 if (read_ahead()) { 730 if (read_ahead()) {
src/ctbutterfly/ctbutterfly.hpp
@@ -92,6 +92,7 @@ protected: @@ -92,6 +92,7 @@ protected:
92 double m_confidence; //!< Confidence level 92 double m_confidence; //!< Confidence level
93 int m_max_iter; //!< Maximum number of iterations 93 int m_max_iter; //!< Maximum number of iterations
94 bool m_apply_edisp; //!< Apply energy dispersion? 94 bool m_apply_edisp; //!< Apply energy dispersion?
  95 + bool m_gepmode; //!< Switch to gaussian error propagation scheme
95 bool m_fit; //!< Do fit? 96 bool m_fit; //!< Do fit?
96 GEbounds m_ebounds; //!< Energy binning definition 97 GEbounds m_ebounds; //!< Energy binning definition
97 GFilename m_outfile; //!< Output ASCII file name 98 GFilename m_outfile; //!< Output ASCII file name
src/ctbutterfly/ctbutterfly.par
@@ -27,6 +27,7 @@ caldb, s, a, &quot;prod2&quot;,,, &quot;Calibration database&quot; @@ -27,6 +27,7 @@ caldb, s, a, &quot;prod2&quot;,,, &quot;Calibration database&quot;
27 irf, s, a, "South_0.5h",,, "Instrument response function" 27 irf, s, a, "South_0.5h",,, "Instrument response function"
28 edisp, b, h, no,,, "Apply energy dispersion?" 28 edisp, b, h, no,,, "Apply energy dispersion?"
29 outfile, f, a, "butterfly.txt",,, "Output ASCII file" 29 outfile, f, a, "butterfly.txt",,, "Output ASCII file"
  30 +gepmode, b, h, no,,, "Use Gaussian Error Propagation"
30 31
31 # 32 #
32 # Butterfly parameters 33 # Butterfly parameters