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 243 m_obs.optimize(m_opt);
244 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 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 255 // Write optimizer, optimised model and covariance matrix into logger
253 256 log_string(NORMAL, m_opt.print(m_chatter));
254 257 log_string(NORMAL, m_obs.models().print(m_chatter));
... ... @@ -271,10 +274,13 @@ void ctbutterfly::run(void)
271 274 GObservations::likelihood likelihood = m_obs.function();
272 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 285 // Write covariance matrix into logger
280 286 log_string(EXPLICIT, m_covariance.print(m_chatter));
... ... @@ -284,105 +290,130 @@ void ctbutterfly::run(void)
284 290 // Write header into logger
285 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 417 for (int k = 0; k < m_ebounds.size(); ++k) {
387 418  
388 419 // Get the energy of current bin
... ... @@ -392,37 +423,112 @@ void ctbutterfly::run(void)
392 423 // Evaluate power law
393 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 532 // Write header into logger
427 533 log_header1(TERSE, "Results");
428 534  
... ... @@ -434,9 +540,11 @@ void ctbutterfly::run(void)
434 540 gammalib::str(m_intensities[k])+" ("+
435 541 gammalib::str(m_min_intensities[k])+", "+
436 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 548 // Restore energy dispersion flag for all CTA observations
441 549 for (int i = 0; i < m_obs.size(); ++i) {
442 550 GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
... ... @@ -507,6 +615,7 @@ void ctbutterfly::init_members(void)
507 615 m_confidence = 0.68;
508 616 m_max_iter = 50;
509 617 m_apply_edisp = false;
  618 + m_gepmode = false;
510 619 m_fit = false;
511 620 m_chatter = static_cast<GChatter>(2);
512 621 m_ebounds.clear();
... ... @@ -540,6 +649,7 @@ void ctbutterfly::copy_members(const ctbutterfly&amp; app)
540 649 m_confidence = app.m_confidence;
541 650 m_max_iter = app.m_max_iter;
542 651 m_apply_edisp = app.m_apply_edisp;
  652 + m_gepmode = app.m_gepmode;
543 653 m_fit = app.m_fit;
544 654 m_chatter = app.m_chatter;
545 655 m_ebounds = app.m_ebounds;
... ... @@ -590,9 +700,6 @@ void ctbutterfly::get_parameters(void)
590 700 // Get name of test source
591 701 m_srcname = (*this)["srcname"].string();
592 702  
593   - // Check model name and type
594   - check_model();
595   -
596 703 // Create energy boundaries from user parameters
597 704 m_ebounds = create_ebounds();
598 705  
... ... @@ -608,10 +715,16 @@ void ctbutterfly::get_parameters(void)
608 715  
609 716 // Get remaining parameters
610 717 m_apply_edisp = (*this)["edisp"].boolean();
  718 + m_gepmode = (*this)["gepmode"].boolean();
611 719 m_confidence = (*this)["confidence"].real();
612 720 m_fit = (*this)["fit"].boolean();
613 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 728 // Optionally read ahead parameters so that they get correctly
616 729 // dumped into the log file
617 730 if (read_ahead()) {
... ...
src/ctbutterfly/ctbutterfly.hpp
... ... @@ -92,6 +92,7 @@ protected:
92 92 double m_confidence; //!< Confidence level
93 93 int m_max_iter; //!< Maximum number of iterations
94 94 bool m_apply_edisp; //!< Apply energy dispersion?
  95 + bool m_gepmode; //!< Switch to gaussian error propagation scheme
95 96 bool m_fit; //!< Do fit?
96 97 GEbounds m_ebounds; //!< Energy binning definition
97 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 27 irf, s, a, "South_0.5h",,, "Instrument response function"
28 28 edisp, b, h, no,,, "Apply energy dispersion?"
29 29 outfile, f, a, "butterfly.txt",,, "Output ASCII file"
  30 +gepmode, b, h, no,,, "Use Gaussian Error Propagation"
30 31  
31 32 #
32 33 # Butterfly parameters
... ...