Feature #1014

Implement GModelSpectralGauss

Added by Deil Christoph over 10 years ago. Updated over 10 years ago.

Status:ClosedStart date:12/03/2013
Priority:NormalDue date:
Assigned To:Owen Ellis% Done:

100%

Category:-
Target version:00-08-00
Duration:

Description

To be able to test energy resolution (#1036) having a line-like spectral model is important.

The Fermi Science tools have a Gaussian: http://fermi.gsfc.nasa.gov/ssc/data/analysis/scitools/source_models.html#Gaussian

In the HESS software we have a TopHat:

/*! \class TopHat  
    A "top-hat" FitFunction.
    \f[ f(E) = C \frac{1}{2} \left(Erf\left(\frac{\ln(E)-\mu_{On}}{\sigma_{On}}\right)+1\right) 
    \left(1- \frac{1}{2} \left(Erf\left(\frac{\ln(E)-\mu_{Off}}{\sigma_{Off}}\right)+1\right) \f]
    Parameters : 
    - #0 "Norm" C
    - #1 "OnLoc" \f$\mu_{On}\f$
    - #2 "OnWidth" \f$\sigma_{On}\f$
    - #3 "OffLoc" \f$\mu_{Off}\f$
    - #4 "OffWidth" \f$\sigma_{Off}\f$
  */

If no-one gets to it before, I could implemented and test this at the January coding sprint.

gauss.png (47.7 KB) Knödlseder Jürgen, 01/17/2014 02:07 AM

Gauss

Recurrence

No recurrence.

History

#1 Updated by Deil Christoph over 10 years ago

  • Description updated (diff)
  • Assigned To set to Owen Ellis
  • Priority changed from Low to Normal

#2 Updated by Deil Christoph over 10 years ago

  • Status changed from New to In Progress
  • Assigned To changed from Owen Ellis to Deil Christoph

Ellis, Chia-Chun and I are currently implementing this here (work in progress):
https://github.com/cdeil/gammalib/compare/issue_1014

#3 Updated by Deil Christoph over 10 years ago

Jürgen, how should we implement GModelSpectralGauss::mc?
(With Google we didn’t find code that works for a given (min, max) energy range, just simple methods for all energies)

#4 Updated by Knödlseder Jürgen over 10 years ago

Deil Christoph wrote:

Jürgen, how should we implement GModelSpectralGauss::mc?
(With Google we didn’t find code that works for a given (min, max) energy range, just simple methods for all energies)

I guess you then use a rejection method. Build a do-while loop around the random number generator and exit only if the energy is within the valid range. This can of course be costly in case that the energy range is narrow, but I guess very often min and max will enclose the bulk of the distribution.

#5 Updated by Owen Ellis over 10 years ago

  • Assigned To changed from Deil Christoph to Owen Ellis
  • Target version set to 2nd coding sprint

#6 Updated by Owen Ellis over 10 years ago

Progress update - implemented so far:

- GModelSpectralGauss::flux

Still to implement (in progress):

- GModelSpectralGauss::write (as in GModelSpectralExpPlaw::write)
- GModelSpectralGauss::flux (as used in Fermi Science Tools)
- GModelSpectralGauss::mc (no progress yet made)

#7 Updated by Deil Christoph over 10 years ago

  • Subject changed from Add GModelSpectralGaussian or GModelSpectralTopHat to Implement GModelSpectralGauss

#8 Updated by Owen Ellis over 10 years ago

  • Status changed from In Progress to Pull request

#9 Updated by Owen Ellis over 10 years ago

  • Status changed from Pull request to In Progress

I accidentally commited files that don’t belong in the repo.
I’ll make a new branch, re-add things and change the issue to “Pull request” once it’s done.

#10 Updated by Owen Ellis over 10 years ago

  • Status changed from In Progress to Pull request

#11 Updated by Owen Ellis over 10 years ago

Also, I noticed the Doxygen docstrings in some cases aren’t formatted correctly - I can correct these tomorrow if neccessary!

#13 Updated by Knödlseder Jürgen over 10 years ago

  • % Done changed from 0 to 50

I’m about to merge in this one.

While going over the code I found that there are no analytical gradients coded for the Gaussian. I guess it won’t be too difficult to include the relevant code. For the performance of the code and the precision of the results this would be crucial. Numerical gradients should only be used when there is no obvious way how to compute the analytical ones.

#14 Updated by Knödlseder Jürgen over 10 years ago

  • Status changed from Pull request to In Progress

Merged into devel.

I put the status back to In Progress to not forget the implementation of the analytical gradients.

What we should also do is a pull distribution test (using for example the cspull script) to make sure that the MC and the model are consistent. See for example https://cta-redmine.irap.omp.eu/projects/gammalib/wiki/GModelSpectralPlaw for a pull distribution for the power law.

#15 Updated by Deil Christoph over 10 years ago

The GModelSpectralGauss::mc() method needs to be improved.

This shows that currently it sometimes generates negative energies (even though I pass a positive emin) and that it sometimes goes into an infinite loop:

import gammalib
model = gammalib.GModelSpectralGauss()
print(model)
emin = gammalib.GEnergy(1, 'MeV')
emax = gammalib.GEnergy(1e6, 'MeV')
time = gammalib.GTime()
ran = gammalib.GRan()
model.mc(emin, emax, time, ran)
print(model.mc(emin, emax, time, ran)) # call this repeatedly

#16 Updated by Knödlseder Jürgen over 10 years ago

I was checking on the web: http://www.design.caltech.edu/erik/Misc/Gaussian.html

The algorithm given is

float x1, x2, w, y1, y2;

         do {
                 x1 = 2.0 * ranf() - 1.0;
                 x2 = 2.0 * ranf() - 1.0;
                 w = x1 * x1 + x2 * x2;
         } while ( w >= 1.0 );

         w = sqrt( (-2.0 * log( w ) ) / w );
         y1 = x1 * w;
         y2 = x2 * w;

The implement algorithm is

    // Get uniform value between 0 and 1
    double u = ran.uniform();

    // Do ...
    do {
        double x1;
        double w;
        do{
            x1        = 2.0 * u - 1.0;
            double x2 = 2.0 * u - 1.0;
            w         = x1 * x1 + x2 * x2;
        } while (w >= 1.0);

        // Do ...
        w = std::sqrt( (-2.0 * std::log( w ) ) / w );

        // Compute ...
        double val = x1 * w;

        // Map into [emin,emax] range
        energy = (xmax - xmin) * val + xmin;

    } while ((xmin <= energy) && (energy < xmax));

One obvious difference is that the implemented version uses the same random number u while the Box-Muller transformation is expecting two independent random numbers. I’m also not sure that the mapping is correct. I think the value should be multiplied by the Gaussian sigma and adding the mean as offset. I guess the code should thus be:
    // Do ...
    do {
        double x1;
        double w;
        do{
            x1        = 2.0 * ran.uniform() - 1.0;
            double x2 = 2.0 * ran.uniform() - 1.0;
            w         = x1 * x1 + x2 * x2;
        } while (w >= 1.0);

        // Do ...
        w = std::sqrt( (-2.0 * std::log( w ) ) / w );

        // Compute ...
        double val = x1 * w;

        // Map to Gaussian parameters
        energy = m_sigma.value() * val + m_mean.value();

    } while ((xmin <= energy) && (energy < xmax));

Can you please check?

#17 Updated by Owen Ellis over 10 years ago

Work in progress: https://github.com/ellisowen/gammalib/tree/issue_1014_gradients

Implemented analytical gradients (I still need to check/test this)

Also made changes suggested by Jürgen to the mc algorithm in the GModelSpectralGauss::mc() method (although I may make further changes due to the issues Christoph mentioned)

I also still need to add GModelSpectralGauss to the user manual and work on the pull distribution test(tomorrow)

#18 Updated by Knödlseder Jürgen over 10 years ago

I’d like now to freeze the code for the gammalib-00-08-00 release. Any progress on the remaining issues? Do you think things will be finished by the end of this week?

#19 Updated by Knödlseder Jürgen over 10 years ago

I went ahead and integrated your actual branch in integration.

I recognized that there was an error in the mc() method that led to the endless loops: the while condition checked whether the energy was within the valid range, and not outside. So it continued looping until an energy outside [emin, emax] was found. The correct code is

    // Sample until we find a value within the requested energy range
    do {
        double x1;
        double w;
        do {
            x1        = 2.0 * ran.uniform() - 1.0;
            double x2 = 2.0 * ran.uniform() - 1.0;
            w         = x1 * x1 + x2 * x2;
        } while (w >= 1.0);

        // Compute random value
        double val = x1 * std::sqrt1 / w);

        // Scale to specified width and shift by mean value
        energy = m_sigma.value() * val + m_mean.value();

    } while (energy < xmin || energy > xmax);

In eval_gradients(), the gradients were formally correct, but there were two issues. First, gradients have to be computed with respect to the value factor and not the factor. A parameter value is the product between a scale and a value factor. The fit changes the value factor, hence gradients are needed for this component. This comes down to multiplying all gradients by the parameter scale. Furthermore, parameters which should not be fit need a zero gradient. Thus, the correct code is

    double g_norm  = (m_norm.is_free()) ? term2 * eterm3 * m_norm.scale() : 0.0;
    double g_mean  = (m_mean.is_free()) ? value * term4 * m_mean.scale() : 0.0;
    double g_sigma = (m_sigma.is_free()) ? -term5 * eterm3 * (1.0 - (2.0 * term3)) * m_sigma.scale() : 0.0;

I also noted that you called the normalization of the Gaussian “Prefactor” in the XML file and gave it a unit of ph/cm2/s/MeV. However, as it’s coded, the parameter is the normalization in units of ph/cm2/s. I thus renamed the parameter to “Normalization” and changed the unit.

I checked the new code by doing a simulation of a Gaussian at 5 TeV. Here the result:

Looks quite reasonable. Now it remains to be see whether the fit works properly. This is done using a pull distribution.

1 -2.0 * std::log(w

#20 Updated by Knödlseder Jürgen over 10 years ago

  • % Done changed from 50 to 60

Merged into devel. Please start from there if you do further changes ...

#21 Updated by Knödlseder Jürgen over 10 years ago

  • % Done changed from 60 to 90

I started doing some pull distributions, and they look good. Will post when finished, but I think we can consider the feature as settled.

#22 Updated by Owen Ellis over 10 years ago

Many thanks for your helpful comments! I think from my side there were no further changes or additions I planned to make.

#23 Updated by Knödlseder Jürgen over 10 years ago

Okay. I guess the only thing still missing is a description of the model in the User Manual. A sub section would be needed in

doc/source/user_manual/modules/model.rst

Could you quickly write something up?

#24 Updated by Owen Ellis over 10 years ago

Sure - I will do it this afternoon

#25 Updated by Owen Ellis over 10 years ago

  • Status changed from In Progress to Pull request

#26 Updated by Knödlseder Jürgen over 10 years ago

  • Status changed from Pull request to Feedback
  • Target version changed from 2nd coding sprint to 00-08-00
  • % Done changed from 90 to 100

Great. Merged this in.

On my side, the pull distributions finished. Everything looks good. They are at GModelSpectralGauss.

You model will go in the GammaLib-00-08-00 release. Congrats.

#27 Updated by Knödlseder Jürgen over 10 years ago

  • Status changed from Feedback to Closed

Also available in: Atom PDF