Example #1
0
TEST(ModelUtil, hessian_times_vector) {
  
  int dim = 5;
  
  Eigen::VectorXd x(dim);
  Eigen::VectorXd v(dim);
  double f;
  Eigen::VectorXd hess_f_dot_v(dim);
  
  std::fstream data_stream(std::string("").c_str(), std::fstream::in);
  stan::io::dump data_var_context(data_stream);
  data_stream.close();
  
  valid_model_namespace::valid_model valid_model(data_var_context, &std::cout);
  EXPECT_NO_THROW(stan::model::hessian_times_vector(valid_model, x, v, f, hess_f_dot_v));
  
  EXPECT_FLOAT_EQ(dim, x.size());
  EXPECT_FLOAT_EQ(dim, v.size());
  EXPECT_FLOAT_EQ(dim, hess_f_dot_v.size());
  
  // Incorporate once operands and partials has been generalized
  //domain_fail_namespace::domain_fail domain_fail_model(data_var_context, &std::cout);
  //EXPECT_THROW(stan::model::hessian_times_vector(domain_fail_model, x, v, f, hess_f_dot_v),
  //             std::domain_error);
  
}
Example #2
0
TEST(ModelUtil, grad_tr_mat_times_hessian) {
  
  int dim = 5;
  
  Eigen::VectorXd x(dim);
  Eigen::MatrixXd X = Eigen::MatrixXd::Identity(dim, dim);
  Eigen::VectorXd grad_tr_X_hess_f(dim);
  
  std::fstream data_stream(std::string("").c_str(), std::fstream::in);
  stan::io::dump data_var_context(data_stream);
  data_stream.close();
  
  valid_model_namespace::valid_model valid_model(data_var_context, &std::cout);
  EXPECT_NO_THROW(stan::model::grad_tr_mat_times_hessian(valid_model, x, X, grad_tr_X_hess_f));
  
  EXPECT_FLOAT_EQ(dim, x.size());
  EXPECT_FLOAT_EQ(dim, X.rows());
  EXPECT_FLOAT_EQ(dim, X.cols());
  EXPECT_FLOAT_EQ(dim, grad_tr_X_hess_f.size());
  
  // Incorporate once operands and partials has been generalized
  //domain_fail_namespace::domain_fail domain_fail_model(data_var_context, &std::cout);
  //EXPECT_THROW(stan::model::grad_tr_mat_times_hessian(domain_fail_model, x, X, grad_tr_X_hess_f),
  //             std::domain_error);
  
}
Example #3
0
/***********************************************************************//**
 * @brief Return spatially integrated sky model
 *
 * @param[in] obsEng Measured photon energy.
 * @param[in] obsTime Measured photon arrival time.
 * @param[in] obs Observation.
 *
 * @exception GException::no_response
 *            No valid instrument response function defined.
 *
 * Computes
 * \f[N"_{\rm pred} = \int_{\rm ROI}
 *    S(\vec{p}, E, t) PSF(\vec{p'}, E', t' | \vec{d}, \vec{p}, E, t) \,
 *    {\rm d}\vec{p'}\f]
 * where
 * \f$S(\vec{p}, E, t)\f$ is the source model,
 * \f$PSF(\vec{p'}, E', t' | \vec{d}, \vec{p}, E, t)\f$ is the point
 * spread function,
 * \f$\vec{p'}\f$ is the measured photon direction,
 * \f$E'\f$ is the measured photon energy,
 * \f$t'\f$ is the measured photon arrival time,
 * \f$\vec{p}\f$ is the true photon arrival direction,
 * \f$E\f$ is the true photon energy,
 * \f$t\f$ is the true photon arrival time, and
 * \f$d\f$ is the instrument pointing.
 *
 * \f${\rm ROI}\f$ is the region of interest that is stored in the
 * GObservation::m_roi member. The integration over the ROI is performed
 * by the GResponse::npred() method.
 *
 * @todo The actual method is only correct if no energy and time dispersion
 *       exists. For the moment we set srcEng=obsEng and srcTime=obsTime.
 *       Formally, Equation (2) of the instrument document has to be
 *       computed, which is an integration over source energy, time
 *       and arrival direction. For the moment, only the integration over
 *       arrival direction is performed by GResponse::npred().
 ***************************************************************************/
double GModelSky::npred(const GEnergy& obsEng, const GTime& obsTime,
                        const GObservation& obs) const
{
    // Initialise result
    double npred = 0.0;

    // Continue only if model is valid)
    if (valid_model()) {

        // Get response function
        GResponse* rsp = obs.response();
        if (rsp == NULL) {
            throw GException::no_response(G_NPRED);
        }

        // Here we make the simplifying approximations
        // srcEng=obsEng and srcTime=obsTime. To be fully correct we should
        // integrate over true energy and true time here ... at least true
        // time if we want to consider energy dispersion ...
        GEnergy srcEng  = obsEng;
        GTime   srcTime = obsTime;

        // Set source
        GSource source(this->name(), *m_spatial, srcEng, srcTime);

        // Compute response components
        double npred_spatial  = rsp->npred(source, obs);
        double npred_spectral = spectral()->eval(srcEng);
        double npred_temporal = temporal()->eval(srcTime);

        // Compute response
        npred = npred_spatial * npred_spectral * npred_temporal;

        // Compile option: Check for NaN/Inf
#if defined(G_NAN_CHECK)
        if (isnotanumber(npred) || isinfinite(npred)) {
            std::cout << "*** ERROR: GModelSky::npred:";
            std::cout << " NaN/Inf encountered";
            std::cout << " (npred=" << npred;
            std::cout << ", npred_spatial=" << npred_spatial;
            std::cout << ", npred_spectral=" << npred_spectral;
            std::cout << ", npred_temporal=" << npred_temporal;
            std::cout << ", srcEng=" << srcEng;
            std::cout << ", srcTime=" << srcTime;
            std::cout << ")" << std::endl;
        }
#endif

    } // endif: model was valid

    // Return npred
    return npred;
}
Example #4
0
TEST(ModelUtil, gradient) {
  
  int dim = 5;
  
  Eigen::VectorXd x(dim);
  double f;
  Eigen::VectorXd g(dim);
  
  std::fstream data_stream(std::string("").c_str(), std::fstream::in);
  stan::io::dump data_var_context(data_stream);
  data_stream.close();
  
  valid_model_namespace::valid_model valid_model(data_var_context, &std::cout);
  EXPECT_NO_THROW(stan::model::gradient(valid_model, x, f, g));
  
  EXPECT_FLOAT_EQ(dim, x.size());
  EXPECT_FLOAT_EQ(dim, g.size());
  
  // Incorporate once operands and partials has been generalized
  //domain_fail_namespace::domain_fail domain_fail_model(data_var_context, &std::cout);
  //EXPECT_THROW(stan::model::gradient(domain_fail_model, x, f, g), std::domain_error);
  
}
Example #5
0
/***********************************************************************//**
 * @brief Return spatially integrated background model
 *
 * @param[in] obsEng Measured event energy.
 * @param[in] obsTime Measured event time.
 * @param[in] obs Observation.
 * @return Spatially integrated model.
 *
 * @exception GException::invalid_argument
 *            The specified observation is not a CTA observation.
 *
 * Spatially integrates the cube background model for a given measured event
 * energy and event time. This method also applies a deadtime correction
 * factor, so that the normalization of the model is a real rate
 * (counts/MeV/s).
 ***************************************************************************/
double GCTAModelCubeBackground::npred(const GEnergy&      obsEng,
                                      const GTime&        obsTime,
                                      const GObservation& obs) const
{
    // Initialise result
    double npred     = 0.0;
    bool   has_npred = false;

    // Build unique identifier
    std::string id = obs.instrument() + "::" + obs.id();

    // Check if Npred value is already in cache
    #if defined(G_USE_NPRED_CACHE)
    if (!m_npred_names.empty()) {

        // Search for unique identifier, and if found, recover Npred value
        // and break
        for (int i = 0; i < m_npred_names.size(); ++i) {
            if (m_npred_names[i] == id && m_npred_energies[i] == obsEng) {
                npred     = m_npred_values[i];
                has_npred = true;
                #if defined(G_DEBUG_NPRED)
                std::cout << "GCTAModelCubeBackground::npred:";
                std::cout << " cache=" << i;
                std::cout << " npred=" << npred << std::endl;
                #endif
                break;
            }
        }

    } // endif: there were values in the Npred cache
    #endif

    // Continue only if no Npred cache value has been found
    if (!has_npred) {

        // Evaluate only if model is valid
        if (valid_model()) {

            // Get pointer on CTA observation
            const GCTAObservation* cta = dynamic_cast<const GCTAObservation*>(&obs);
            if (cta == NULL) {
                std::string msg = "Specified observation is not a CTA"
                                  " observation.\n" + obs.print();
                throw GException::invalid_argument(G_NPRED, msg);
            }

            // Get pointer on CTA cube response
            const GCTAResponseCube* rsp = dynamic_cast<const GCTAResponseCube*>(cta->response());
            if (rsp == NULL) {
                std::string msg = "Specified observation does not contain"
                                  " a cube response.\n" + obs.print();
                throw GException::invalid_argument(G_NPRED, msg);
            }

            // Get log10 of energy in TeV
            double logE = obsEng.log10TeV();

            // Retrieve CTA background
            const GCTACubeBackground bgd = rsp->background();

            // Integrate the background map at a certain energy
            npred = bgd.integral(logE);

            // Store result in Npred cache
            #if defined(G_USE_NPRED_CACHE)
            m_npred_names.push_back(id);
            m_npred_energies.push_back(obsEng);
            m_npred_times.push_back(obsTime);
            m_npred_values.push_back(npred);
            #endif

            // Debug: Check for NaN
            #if defined(G_NAN_CHECK)
            if (gammalib::is_notanumber(npred) || gammalib::is_infinite(npred)) {
                std::string origin  = "GCTAModelCubeBackground::npred";
                std::string message = " NaN/Inf encountered (npred=" +
                                      gammalib::str(npred) + ")";
                gammalib::warning(origin, message);
            }
            #endif

        } // endif: model was valid

    } // endif: Npred computation required

    // Multiply in spectral and temporal components
    npred *= spectral()->eval(obsEng, obsTime);
    npred *= temporal()->eval(obsTime);

    // Apply deadtime correction
    npred *= obs.deadc(obsTime);

    // Return Npred
    return npred;
}
/***********************************************************************//**
 * @brief Return simulated list of events
 *
 * @param[in] obs Observation.
 * @param[in] ran Random number generator.
 * @return Pointer to list of simulated events (needs to be de-allocated by
 *         client)
 *
 * @exception GException::invalid_argument
 *            No CTA event list found in observation.
 *
 * Draws a sample of events from the background model using a Monte
 * Carlo simulation. The pointing information, the energy boundaries and the
 * good time interval for the sampling will be extracted from the observation
 * argument that is passed to the method. The method also requires a random
 * number generator of type GRan which is passed by reference, hence the
 * state of the random number generator will be changed by the method.
 *
 * The method also applies a deadtime correction using a Monte Carlo process,
 * taking into account temporal deadtime variations. For this purpose, the
 * method makes use of the time dependent GObservation::deadc method.
 ***************************************************************************/
GCTAEventList* GCTAModelBackground::mc(const GObservation& obs, GRan& ran) const
{
    // Initialise new event list
    GCTAEventList* list = new GCTAEventList;

    // Continue only if model is valid)
    if (valid_model()) {

        // Extract event list to access the ROI, energy boundaries and GTIs
        const GCTAEventList* events = dynamic_cast<const GCTAEventList*>(obs.events());
        if (events == NULL) {
            std::string msg = "No CTA event list found in observation.\n" +
                              obs.print();
            throw GException::invalid_argument(G_MC, msg);
        }

        // Get simulation region
        const GCTARoi&  roi     = events->roi();
        const GEbounds& ebounds = events->ebounds();
        const GGti&     gti     = events->gti();

        // Set simulation region for result event list
        list->roi(roi);
        list->ebounds(ebounds);
        list->gti(gti);

        // Loop over all energy boundaries
        for (int ieng = 0; ieng < ebounds.size(); ++ieng) {

            // Initialise de-allocation flag
            bool free_spectral = false;

            // Set pointer to spectral model
            GModelSpectral* spectral = m_spectral;

            // If the spectral model is a diffuse cube then create a node
            // function spectral model that is the product of the diffuse
            // cube node function and the spectral model evaluated at the
            // energies of the node function
            GModelSpatialDiffuseCube* cube =
                dynamic_cast<GModelSpatialDiffuseCube*>(m_spatial);
            if (cube != NULL) {

			   // Set MC simulation cone based on ROI
			   cube->set_mc_cone(roi.centre().dir(), roi.radius());

			   // Allocate node function to replace the spectral component
			   GModelSpectralNodes* nodes = new GModelSpectralNodes(cube->spectrum());
			   for (int i = 0; i < nodes->nodes(); ++i) {
				   GEnergy energy    = nodes->energy(i);
				   double  intensity = nodes->intensity(i);
				   double  norm      = m_spectral->eval(energy, events->tstart());
				   nodes->intensity(i, norm*intensity);
			   }

			   // Signal that node function needs to be de-allocated later
			   free_spectral = true;

			   // Set the spectral model pointer to the node function
			   spectral = nodes;

            } // endif: spatial model was a diffuse cube

            // Compute the background rate in model within the energy boundaries
            // from spectral component (units: cts/s).
            // Note that the time here is ontime. Deadtime correction will be done
            // later.
            double rate = spectral->flux(ebounds.emin(ieng), ebounds.emax(ieng));

            // Debug option: dump rate
            #if defined(G_DUMP_MC)
            std::cout << "GCTAModelBackground::mc(\"" << name() << "\": ";
            std::cout << "rate=" << rate << " cts/s)" << std::endl;
            #endif

            // Loop over all good time intervals
            for (int itime = 0; itime < gti.size(); ++itime) {

                // Get Monte Carlo event arrival times from temporal model
                GTimes times = m_temporal->mc(rate,
                                              gti.tstart(itime),
                                              gti.tstop(itime),
                                              ran);

                // Get number of events
                int n_events = times.size();

                // Reserve space for events
                if (n_events > 0) {
                    list->reserve(n_events);
                }

                // Loop over events
                for (int i = 0; i < n_events; ++i) {

                    // Apply deadtime correction
                    double deadc = obs.deadc(times[i]);
                    if (deadc < 1.0) {
                        if (ran.uniform() > deadc) {
                            continue;
                        }
                    }

                    // Get Monte Carlo event energy from spectral model
                    GEnergy energy = spectral->mc(ebounds.emin(ieng),
                                                  ebounds.emax(ieng),
                                                  times[i],
                                                  ran);

                    // Get Monte Carlo event direction from spatial model
                    GSkyDir dir = spatial()->mc(energy, times[i], ran);

                    // Allocate event
                    GCTAEventAtom event;

                    // Set event attributes
                    event.dir(GCTAInstDir(dir));
                    event.energy(energy);
                    event.time(times[i]);

                    // Append event to list if it falls in ROI
                    if (events->roi().contains(event)) {
                        list->append(event);
                    }

                } // endfor: looped over all events

            } // endfor: looped over all GTIs

            // Free spectral model if required
            if (free_spectral) delete spectral;

        } // endfor: looped over all energy boundaries

    } // endif: model was valid

    // Return
    return list;
}
/***********************************************************************//**
 * @brief Return spatially integrated data model
 *
 * @param[in] obsEng Measured event energy.
 * @param[in] obsTime Measured event time.
 * @param[in] obs Observation.
 * @return Spatially integrated model.
 *
 * @exception GException::invalid_argument
 *            No CTA event list found in observation.
 *            No CTA pointing found in observation.
 *
 * Spatially integrates the data model for a given measured event energy and
 * event time. This method also applies a deadtime correction factor, so that
 * the normalization of the model is a real rate (counts/exposure time).
 ***************************************************************************/
double GCTAModelBackground::npred(const GEnergy&      obsEng,
                                  const GTime&        obsTime,
                                  const GObservation& obs) const
{
    // Initialise result
    double npred     = 0.0;
    bool   has_npred = false;

    // Build unique identifier
    std::string id = obs.instrument() + "::" + obs.id();

    // Check if Npred value is already in cache
    #if defined(G_USE_NPRED_CACHE)
    if (!m_npred_names.empty()) {

        // Search for unique identifier, and if found, recover Npred value
		// and break
		for (int i = 0; i < m_npred_names.size(); ++i) {
			if (m_npred_names[i] == id && m_npred_energies[i] == obsEng) {
				npred     = m_npred_values[i];
				has_npred = true;
				#if defined(G_DEBUG_NPRED)
				std::cout << "GCTAModelBackground::npred:";
				std::cout << " cache=" << i;
				std::cout << " npred=" << npred << std::endl;
				#endif
				break;
			}
		}

    } // endif: there were values in the Npred cache
    #endif

    // Continue only if no Npred cache value was found
    if (!has_npred) {

        // Evaluate only if model is valid
        if (valid_model()) {

            // Get CTA event list
			const GCTAEventList* events = dynamic_cast<const GCTAEventList*>(obs.events());
            if (events == NULL) {
                std::string msg = "No CTA event list found in observation.\n" +
                                  obs.print();
                throw GException::invalid_argument(G_NPRED, msg);
            }

            #if !defined(G_NPRED_AROUND_ROI)
			// Get CTA pointing direction
			GCTAPointing* pnt = dynamic_cast<GCTAPointing*>(obs.pointing());
            if (pnt == NULL) {
                std::string msg = "No CTA pointing found in observation.\n" +
                                  obs.print();
                throw GException::invalid_argument(G_NPRED, msg);
            }
            #endif

            // Get reference to ROI centre
            const GSkyDir& roi_centre = events->roi().centre().dir();

			// Get ROI radius in radians
			double roi_radius = events->roi().radius() * gammalib::deg2rad;

			// Get distance from ROI centre in radians
            #if defined(G_NPRED_AROUND_ROI)
			double roi_distance = 0.0;
            #else
			double roi_distance = roi_centre.dist(pnt->dir());
            #endif

			// Initialise rotation matrix to transform from ROI system to
            // celestial coordinate system
			GMatrix ry;
			GMatrix rz;
			ry.eulery(roi_centre.dec_deg() - 90.0);
			rz.eulerz(-roi_centre.ra_deg());
			GMatrix rot = (ry * rz).transpose();

			// Compute position angle of ROI centre with respect to model
			// centre (radians)
            #if defined(G_NPRED_AROUND_ROI)
            double omega0 = 0.0;
            #else
			double omega0 = pnt->dir().posang(events->roi().centre().dir());
            #endif

			// Setup integration function
			GCTAModelBackground::npred_roi_kern_theta integrand(spatial(),
                                                                obsEng,
                                                                obsTime,
                                                                rot,
                                                                roi_radius,
                                                                roi_distance,
                                                                omega0);

			// Setup integrator
			GIntegral integral(&integrand);
			integral.eps(1e-3);

			// Setup integration boundaries
            #if defined(G_NPRED_AROUND_ROI)
			double rmin = 0.0;
			double rmax = roi_radius;
            #else
			double rmin = (roi_distance > roi_radius) ? roi_distance-roi_radius : 0.0;
			double rmax = roi_radius + roi_distance;
            #endif

			// Spatially integrate radial component
			npred = integral.romb(rmin, rmax);

	        // Store result in Npred cache
	        #if defined(G_USE_NPRED_CACHE)
	        m_npred_names.push_back(id);
	        m_npred_energies.push_back(obsEng);
	        m_npred_times.push_back(obsTime);
	        m_npred_values.push_back(npred);
	        #endif

	        // Debug: Check for NaN
	        #if defined(G_NAN_CHECK)
	        if (gammalib::is_notanumber(npred) || gammalib::is_infinite(npred)) {
	            std::cout << "*** ERROR: GCTAModelBackground::npred:";
	            std::cout << " NaN/Inf encountered";
	            std::cout << " (npred=" << npred;
	            std::cout << ", roi_radius=" << roi_radius;
	            std::cout << ")" << std::endl;
	        }
	        #endif

        } // endif: model was valid

    } // endif: Npred computation required

	// Multiply in spectral and temporal components
	npred *= spectral()->eval(obsEng, obsTime);
	npred *= temporal()->eval(obsTime);

	// Apply deadtime correction
	npred *= obs.deadc(obsTime);

    // Return Npred
    return npred;
}
Example #8
0
/***********************************************************************//**
 * @brief Return simulated list of photons
 *
 * @param[in] area Simulation surface area (cm2).
 * @param[in] dir Centre of simulation cone.
 * @param[in] radius Radius of simulation cone (deg).
 * @param[in] emin Minimum photon energy.
 * @param[in] emax Maximum photon energy.
 * @param[in] tmin Minimum photon arrival time.
 * @param[in] tmax Maximum photon arrival time.
 * @param[in] ran Random number generator.
 *
 * This method returns a list of photons that has been derived by Monte Carlo
 * simulation from the model. A simulation region is define by specification
 * of a simulation cone (a circular region on the sky),
 * of an energy range [emin, emax], and
 * of a time interval [tmin, tmax].
 * The simulation cone may eventually cover the entire sky (by setting
 * the radius to 180 degrees), yet simulations will be more efficient if
 * only the sky region will be simulated that is actually observed by the
 * telescope.
 *
 * @todo Check usage for diffuse models
 * @todo Implement photon arrival direction simulation for diffuse models
 * @todo Implement unique model ID to assign as Monte Carlo ID
 ***************************************************************************/
GPhotons GModelSky::mc(const double& area,
                       const GSkyDir& dir,  const double&  radius,
                       const GEnergy& emin, const GEnergy& emax,
                       const GTime&   tmin, const GTime&   tmax,
                       GRan& ran) const
{
    // Allocate photons
    GPhotons photons;

    // Continue only if model is valid)
    if (valid_model()) {

        // Get point source pointer
        GModelSpatialPtsrc* ptsrc = dynamic_cast<GModelSpatialPtsrc*>(m_spatial);

        // Check if model will produce any photons in the specified
        // simulation region. If the model is a point source we check if the
        // source is located within the simulation code. If the model is a
        // diffuse source we check if the source overlaps with the simulation
        // code
        bool use_model = true;
        if (ptsrc != NULL) {
            if (dir.dist(ptsrc->dir()) > radius) {
                use_model = false;
            }
        }
        else {
            //TODO
        }

        // Continue only if model overlaps with simulation region
        if (use_model) {

            // Compute flux within [emin, emax] in model from spectral
            // component (units: ph/cm2/s)
            double flux = m_spectral->flux(emin, emax);

            // Derive expecting counting rate within simulation surface
            // (units: ph/s)
            double rate = flux * area;

            // Debug option: dump rate
#if G_DUMP_MC
            std::cout << "GModelSky::mc(\"" << name() << "\": ";
            std::cout << "flux=" << flux << " ph/cm2/s, ";
            std::cout << "rate=" << rate << " ph/s)" << std::endl;
#endif

            // Get photon arrival times from temporal model
            GTimes times = m_temporal->mc(rate, tmin, tmax, ran);

            // Reserve space for photons
            if (times.size() > 0) {
                photons.reserve(times.size());
            }

            // Loop over photons
            for (int i = 0; i < times.size(); ++i) {

                // Allocate photon
                GPhoton photon;

                // Set photon arrival time
                photon.time(times[i]);

                // Set incident photon direction
                photon.dir(m_spatial->mc(ran));

                // Set photon energy
                photon.energy(m_spectral->mc(emin, emax, ran));

                // Append photon
                photons.append(photon);

            } // endfor: looped over photons

        } // endif: model was used
    } // endif: model was valid

    // Return photon list
    return photons;
}
/***********************************************************************//**
 * @brief Return simulated list of events
 *
 * @param[in] obs Observation.
 * @param[in] ran Random number generator.
 * @return Pointer to list of simulated events (needs to be de-allocated by
 *         client)
 *
 * @exception GException::invalid_argument
 *            Specified observation is not a CTA observation.
 *
 * Draws a sample of events from the background model using a Monte
 * Carlo simulation. The region of interest, the energy boundaries and the
 * good time interval for the sampling will be extracted from the observation
 * argument that is passed to the method. The method also requires a random
 * number generator of type GRan which is passed by reference, hence the
 * state of the random number generator will be changed by the method.
 *
 * The method also applies a deadtime correction using a Monte Carlo process,
 * taking into account temporal deadtime variations. For this purpose, the
 * method makes use of the time dependent GObservation::deadc method.
 *
 * For each event in the returned event list, the sky direction, the nominal
 * coordinates (DETX and DETY), the energy and the time will be set.
 ***************************************************************************/
GCTAEventList* GCTAModelAeffBackground::mc(const GObservation& obs,
                                           GRan& ran) const
{
    // Initialise new event list
    GCTAEventList* list = new GCTAEventList;

    // Continue only if model is valid)
    if (valid_model()) {

        // Retrieve CTA observation
        const GCTAObservation* cta = dynamic_cast<const GCTAObservation*>(&obs);
        if (cta == NULL) {
            std::string msg = "Specified observation is not a CTA "
                              "observation.\n" + obs.print();
            throw GException::invalid_argument(G_MC, msg);
        }

        // Get pointer on CTA IRF response
        const GCTAResponseIrf* rsp =
              dynamic_cast<const GCTAResponseIrf*>(cta->response());
        if (rsp == NULL) {
            std::string msg = "Specified observation does not contain"
                              " an IRF response.\n" + obs.print();
            throw GException::invalid_argument(G_MC, msg);
        }

        // Retrieve CTA response and pointing
        const GCTAPointing& pnt = cta->pointing();

        // Get pointer to CTA effective area
        const GCTAAeff* aeff = rsp->aeff();
        if (aeff == NULL) {
            std::string msg = "Specified observation contains no effective area"
                              " information.\n" + obs.print();
            throw GException::invalid_argument(G_MC, msg);
        }

        // Retrieve event list to access the ROI, energy boundaries and GTIs
        const GCTAEventList* events =
              dynamic_cast<const GCTAEventList*>(obs.events());
        if (events == NULL) {
            std::string msg = "No CTA event list found in observation.\n" +
                              obs.print();
            throw GException::invalid_argument(G_MC, msg);
        }

        // Get simulation region
        const GCTARoi&  roi     = events->roi();
        const GEbounds& ebounds = events->ebounds();
        const GGti&     gti     = events->gti();

        // Get maximum offset value for simulations
        double max_theta     = pnt.dir().dist(roi.centre().dir()) +
                               roi.radius() * gammalib::deg2rad;
        double cos_max_theta = std::cos(max_theta);

        // Set simulation region for result event list
        list->roi(roi);
        list->ebounds(ebounds);
        list->gti(gti);

        // Set up spectral model to draw random energies from. Here we use
        // a fixed energy sampling for an instance of GModelSpectralNodes.
        // This is analogous to to the GCTAModelIrfBackground::mc method.
        // We make sure that only non-negative nodes get appended.
        GEbounds spectral_ebounds =
            GEbounds(m_n_mc_energies, ebounds.emin(), ebounds.emax(), true);
        GModelSpectralNodes spectral;
        for (int i = 0; i < spectral_ebounds.size(); ++i) {
            GEnergy energy    = spectral_ebounds.elogmean(i);
            double  intensity = aeff_integral(obs, energy.log10TeV());
            double  norm      = m_spectral->eval(energy, events->tstart());
            double  arg       = norm * intensity;
            if (arg > 0.0) {
                spectral.append(energy, arg);
            }
        }

        // Loop over all energy bins
        for (int ieng = 0; ieng < ebounds.size(); ++ieng) {

            // Compute the background rate in model within the energy
            // boundaries from spectral component (units: cts/s).
            // Note that the time here is ontime. Deadtime correction will
            // be done later.
            double rate = spectral.flux(ebounds.emin(ieng), ebounds.emax(ieng));

            // Debug option: dump rate
            #if defined(G_DUMP_MC)
            std::cout << "GCTAModelAeffBackground::mc(\"" << name() << "\": ";
            std::cout << "rate=" << rate << " cts/s)" << std::endl;
            #endif

            // If the rate is not positive then skip this energy bins
            if (rate <= 0.0) {
                continue;
            }

            // Loop over all good time intervals
            for (int itime = 0; itime < gti.size(); ++itime) {

                // Get Monte Carlo event arrival times from temporal model
                GTimes times = m_temporal->mc(rate,
                                              gti.tstart(itime),
                                              gti.tstop(itime),
                                              ran);

                // Get number of events
                int n_events = times.size();

                // Reserve space for events
                if (n_events > 0) {
                    list->reserve(n_events);
                }

                // Debug option: provide number of times and initialize
                // statisics
                #if defined(G_DUMP_MC)
                std::cout << " Interval " << itime;
                std::cout << " times=" << n_events << std::endl;
                int n_killed_by_deadtime = 0;
                int n_killed_by_roi      = 0;
                #endif

                // Loop over events
                for (int i = 0; i < n_events; ++i) {

                    // Apply deadtime correction
                    double deadc = obs.deadc(times[i]);
                    if (deadc < 1.0) {
                        if (ran.uniform() > deadc) {
                            #if defined(G_DUMP_MC)
                            n_killed_by_deadtime++;
                            #endif
                            continue;
                        }
                    }

                    // Get Monte Carlo event energy from spectral model
                    GEnergy energy = spectral.mc(ebounds.emin(ieng),
                                                 ebounds.emax(ieng),
                                                 times[i],
                                                 ran);

                    // Get maximum effective area for rejection method
                    double max_aeff = aeff->max(energy.log10TeV(), pnt.zenith(),
                                                pnt.azimuth(), false);

                    // Skip event if the maximum effective area is not positive
                    if (max_aeff <= 0.0) {
                        continue;
                    }

                    // Initialise randomised coordinates
                    double offset = 0.0;
                    double phi    = 0.0;

                    // Initialise acceptance fraction and counter of zeros for
                    // rejection method
                    double acceptance_fraction = 0.0;
                    int    zeros               = 0;

                    // Start rejection method loop
                    do {

                        // Throw random offset and azimuth angle in
                        // considered range
                        offset = std::acos(1.0 - ran.uniform() *
                                           (1.0 - cos_max_theta));
                        phi    = ran.uniform() * gammalib::twopi;

                        // Compute function value at this offset angle
                        double value = (*aeff)(energy.log10TeV(), offset, phi,
                                               pnt.zenith(), pnt.azimuth(),
                                               false);

                        // If the value is not positive then increment the
                        // zeros counter and fall through. The counter assures
                        // that this loop does not lock up.
                        if (value <= 0.0) {
                            zeros++;
                            continue;
                        }

                        // Value is non-zero so reset the zeros counter
                        zeros = 0;

                        // Compute acceptance fraction
                        acceptance_fraction = value / max_aeff;
			
                    } while ((ran.uniform() > acceptance_fraction) &&
                             (zeros < 1000));

                    // If the zeros counter is non-zero then the loop was
                    // exited due to exhaustion and the event is skipped
                    if (zeros > 0) {
                        continue;
                    }

                    // Convert CTA pointing direction in instrument system
                    GCTAInstDir mc_dir(pnt.dir());

                    // Rotate pointing direction by offset and azimuth angle
                    mc_dir.dir().rotate_deg(phi    * gammalib::rad2deg,
                                            offset * gammalib::rad2deg);

                    // Compute DETX and DETY coordinates
                    double detx(0.0);
                    double dety(0.0);
                    if (offset > 0.0 ) {
                        detx = offset * std::cos(phi);
                        dety = offset * std::sin(phi);
                    }

                    // Set DETX and DETY coordinates
                    mc_dir.detx(detx);
                    mc_dir.dety(dety);

                    // Allocate event
                    GCTAEventAtom event;

                    // Set event attributes
                    event.dir(mc_dir);
                    event.energy(energy);
                    event.time(times[i]);

                    // Append event to list if it falls in ROI
                    if (events->roi().contains(event)) {
                        list->append(event);
                    }
                    #if defined(G_DUMP_MC)
                    else {
                        n_killed_by_roi++;
                    }
                    #endif

                } // endfor: looped over all events

                // Debug option: provide  statisics
                #if defined(G_DUMP_MC)
                std::cout << " Killed by deadtime=";
                std::cout << n_killed_by_deadtime << std::endl;
                std::cout << " Killed by ROI=";
                std::cout << n_killed_by_roi << std::endl;
                #endif

            } // endfor: looped over all GTIs

        } // endfor: looped over all energy boundaries

    } // endif: model was valid

    // Return
    return list;
}
/***********************************************************************//**
 * @brief Return spatially integrated background model
 *
 * @param[in] obsEng Measured event energy.
 * @param[in] obsTime Measured event time.
 * @param[in] obs Observation.
 * @return Spatially integrated model.
 *
 * @exception GException::invalid_argument
 *            The specified observation is not a CTA observation.
 *
 * Spatially integrates the effective area background model for a given
 * measured event energy and event time. This method also applies a deadtime
 * correction factor, so that the normalization of the model is a real rate
 * (counts/MeV/s).
 ***************************************************************************/
double GCTAModelAeffBackground::npred(const GEnergy&      obsEng,
                                      const GTime&        obsTime,
                                      const GObservation& obs) const
{
    // Set number of iterations for Romberg integration.
    //static const int iter_theta = 6;
    //static const int iter_phi   = 6;

    // Initialise result
    double npred     = 0.0;
    bool   has_npred = false;

    // Build unique identifier
    std::string id = obs.instrument() + "::" + obs.id();

    // Check if Npred value is already in cache
    #if defined(G_USE_NPRED_CACHE)
    if (!m_npred_names.empty()) {

        // Search for unique identifier, and if found, recover Npred value
        // and break
        for (int i = 0; i < m_npred_names.size(); ++i) {
            if (m_npred_names[i] == id && m_npred_energies[i] == obsEng) {
                npred     = m_npred_values[i];
                has_npred = true;
                #if defined(G_DEBUG_NPRED)
                std::cout << "GCTAModelAeffBackground::npred:";
                std::cout << " cache=" << i;
                std::cout << " npred=" << npred << std::endl;
                #endif
                break;
            }
        }

    } // endif: there were values in the Npred cache
    #endif

    // Continue only if no Npred cache value has been found
    if (!has_npred) {

        // Evaluate only if model is valid
        if (valid_model()) {

            // Get log10 of energy in TeV
            double logE = obsEng.log10TeV();

            // Spatially integrate effective area component
            npred = this->aeff_integral(obs, logE);

            // Store result in Npred cache
            #if defined(G_USE_NPRED_CACHE)
            m_npred_names.push_back(id);
            m_npred_energies.push_back(obsEng);
            m_npred_times.push_back(obsTime);
            m_npred_values.push_back(npred);
            #endif

            // Debug: Check for NaN
            #if defined(G_NAN_CHECK)
            if (gammalib::is_notanumber(npred) || gammalib::is_infinite(npred)) {
                std::string origin  = "GCTAModelAeffBackground::npred";
                std::string message = " NaN/Inf encountered (npred=" +
                                      gammalib::str(npred) + ")";
                gammalib::warning(origin, message);
            }
            #endif

        } // endif: model was valid

    } // endif: Npred computation required

    // Multiply in spectral and temporal components
    npred *= spectral()->eval(obsEng, obsTime);
    npred *= temporal()->eval(obsTime);

    // Apply deadtime correction
    npred *= obs.deadc(obsTime);

    // Return Npred
    return npred;
}
/***********************************************************************//**
 * @brief Return simulated list of events
 *
 * @param[in] obs Observation.
 * @param[in] ran Random number generator.
 * @return Pointer to list of simulated events (needs to be de-allocated by
 *         client)
 *
 * @exception GException::invalid_argument
 *            Specified observation is not a CTA observation.
 *
 * Draws a sample of events from the background model using a Monte
 * Carlo simulation. The region of interest, the energy boundaries and the
 * good time interval for the sampling will be extracted from the observation
 * argument that is passed to the method. The method also requires a random
 * number generator of type GRan which is passed by reference, hence the
 * state of the random number generator will be changed by the method.
 *
 * The method also applies a deadtime correction using a Monte Carlo process,
 * taking into account temporal deadtime variations. For this purpose, the
 * method makes use of the time dependent GObservation::deadc method.
 *
 * For each event in the returned event list, the sky direction, the nominal
 * coordinates (DETX and DETY), the energy and the time will be set.
 ***************************************************************************/
GCTAEventList* GCTAModelIrfBackground::mc(const GObservation& obs, GRan& ran) const
{
    // Initialise new event list
    GCTAEventList* list = new GCTAEventList;

    // Continue only if model is valid)
    if (valid_model()) {

        // Retrieve CTA observation
        const GCTAObservation* cta = dynamic_cast<const GCTAObservation*>(&obs);
        if (cta == NULL) {
            std::string msg = "Specified observation is not a CTA observation.\n" +
                              obs.print();
            throw GException::invalid_argument(G_MC, msg);
        }

        // Get pointer on CTA IRF response
        const GCTAResponseIrf* rsp = dynamic_cast<const GCTAResponseIrf*>(cta->response());
        if (rsp == NULL) {
            std::string msg = "Specified observation does not contain"
                              " an IRF response.\n" + obs.print();
            throw GException::invalid_argument(G_MC, msg);
        }

        // Retrieve CTA response and pointing
        const GCTAPointing& pnt = cta->pointing();

        // Get pointer to CTA background
        const GCTABackground* bgd = rsp->background();
        if (bgd == NULL) {
            std::string msg = "Specified observation contains no background"
                              " information.\n" + obs.print();
            throw GException::invalid_argument(G_MC, msg);
        }

        // Retrieve event list to access the ROI, energy boundaries and GTIs
        const GCTAEventList* events = dynamic_cast<const GCTAEventList*>(obs.events());
        if (events == NULL) {
            std::string msg = "No CTA event list found in observation.\n" +
                              obs.print();
            throw GException::invalid_argument(G_MC, msg);
        }

        // Get simulation region
        const GCTARoi&  roi     = events->roi();
        const GEbounds& ebounds = events->ebounds();
        const GGti&     gti     = events->gti();

        // Set simulation region for result event list
        list->roi(roi);
        list->ebounds(ebounds);
        list->gti(gti);

        // Create a spectral model that combines the information from the
        // background information and the spectrum provided by the model
        GModelSpectralNodes spectral(bgd->spectrum());
        for (int i = 0; i < spectral.nodes(); ++i) {
            GEnergy energy    = spectral.energy(i);
            double  intensity = spectral.intensity(i);
            double  norm      = m_spectral->eval(energy, events->tstart());
            spectral.intensity(i, norm*intensity);
        }

        // Loop over all energy boundaries
        for (int ieng = 0; ieng < ebounds.size(); ++ieng) {

            // Compute the background rate in model within the energy
            // boundaries from spectral component (units: cts/s).
            // Note that the time here is ontime. Deadtime correction will
            // be done later.
            double rate = spectral.flux(ebounds.emin(ieng), ebounds.emax(ieng));

            // Debug option: dump rate
#if defined(G_DUMP_MC)
            std::cout << "GCTAModelIrfBackground::mc(\"" << name() << "\": ";
            std::cout << "rate=" << rate << " cts/s)" << std::endl;
#endif

            // Loop over all good time intervals
            for (int itime = 0; itime < gti.size(); ++itime) {

                // Get Monte Carlo event arrival times from temporal model
                GTimes times = m_temporal->mc(rate,
                                              gti.tstart(itime),
                                              gti.tstop(itime),
                                              ran);

                // Get number of events
                int n_events = times.size();

                // Reserve space for events
                if (n_events > 0) {
                    list->reserve(n_events);
                }

                // Debug option: provide number of times and initialize
                // statisics
#if defined(G_DUMP_MC)
                std::cout << " Interval " << itime;
                std::cout << " times=" << n_events << std::endl;
                int n_killed_by_deadtime = 0;
                int n_killed_by_roi      = 0;
#endif

                // Loop over events
                for (int i = 0; i < n_events; ++i) {

                    // Apply deadtime correction
                    double deadc = obs.deadc(times[i]);
                    if (deadc < 1.0) {
                        if (ran.uniform() > deadc) {
#if defined(G_DUMP_MC)
                            n_killed_by_deadtime++;
#endif
                            continue;
                        }
                    }

                    // Get Monte Carlo event energy from spectral model
                    GEnergy energy = spectral.mc(ebounds.emin(ieng),
                                                 ebounds.emax(ieng),
                                                 times[i],
                                                 ran);

                    // Get Monte Carlo event direction from spatial model.
                    // This only will set the DETX and DETY coordinates.
                    GCTAInstDir instdir = bgd->mc(energy, times[i], ran);

                    // Derive sky direction from instrument coordinates
                    GSkyDir skydir = pnt.skydir(instdir);

                    // Set sky direction in GCTAInstDir object
                    instdir.dir(skydir);

                    // Allocate event
                    GCTAEventAtom event;

                    // Set event attributes
                    event.dir(instdir);
                    event.energy(energy);
                    event.time(times[i]);

                    // Append event to list if it falls in ROI
                    if (events->roi().contains(event)) {
                        list->append(event);
                    }
#if defined(G_DUMP_MC)
                    else {
                        n_killed_by_roi++;
                    }
#endif

                } // endfor: looped over all events

                // Debug option: provide  statisics
#if defined(G_DUMP_MC)
                std::cout << " Killed by deadtime=";
                std::cout << n_killed_by_deadtime << std::endl;
                std::cout << " Killed by ROI=";
                std::cout << n_killed_by_roi << std::endl;
#endif

            } // endfor: looped over all GTIs

        } // endfor: looped over all energy boundaries

    } // endif: model was valid

    // Return
    return list;
}
/***********************************************************************//**
 * @brief Return spatially integrated background model
 *
 * @param[in] obsEng Measured event energy.
 * @param[in] obsTime Measured event time.
 * @param[in] obs Observation.
 * @return Spatially integrated model.
 *
 * @exception GException::invalid_argument
 *            The specified observation is not a CTA observation.
 *
 * Spatially integrates the instrumental background model for a given
 * measured event energy and event time. This method also applies a deadtime
 * correction factor, so that the normalization of the model is a real rate
 * (counts/MeV/s).
 ***************************************************************************/
double GCTAModelIrfBackground::npred(const GEnergy&      obsEng,
                                     const GTime&        obsTime,
                                     const GObservation& obs) const
{
    // Initialise result
    double npred     = 0.0;
    bool   has_npred = false;

    // Build unique identifier
    std::string id = obs.instrument() + "::" + obs.id();

    // Check if Npred value is already in cache
#if defined(G_USE_NPRED_CACHE)
    if (!m_npred_names.empty()) {

        // Search for unique identifier, and if found, recover Npred value
        // and break
        for (int i = 0; i < m_npred_names.size(); ++i) {
            if (m_npred_names[i] == id && m_npred_energies[i] == obsEng) {
                npred     = m_npred_values[i];
                has_npred = true;
#if defined(G_DEBUG_NPRED)
                std::cout << "GCTAModelIrfBackground::npred:";
                std::cout << " cache=" << i;
                std::cout << " npred=" << npred << std::endl;
#endif
                break;
            }
        }

    } // endif: there were values in the Npred cache
#endif

    // Continue only if no Npred cache value has been found
    if (!has_npred) {

        // Evaluate only if model is valid
        if (valid_model()) {

            // Get pointer on CTA observation
            const GCTAObservation* cta = dynamic_cast<const GCTAObservation*>(&obs);
            if (cta == NULL) {
                std::string msg = "Specified observation is not a CTA"
                                  " observation.\n" + obs.print();
                throw GException::invalid_argument(G_NPRED, msg);
            }

            // Get pointer on CTA IRF response
            const GCTAResponseIrf* rsp = dynamic_cast<const GCTAResponseIrf*>(cta->response());
            if (rsp == NULL) {
                std::string msg = "Specified observation does not contain"
                                  " an IRF response.\n" + obs.print();
                throw GException::invalid_argument(G_NPRED, msg);
            }

            // Retrieve pointer to CTA background
            const GCTABackground* bgd = rsp->background();
            if (bgd == NULL) {
                std::string msg = "Specified observation contains no background"
                                  " information.\n" + obs.print();
                throw GException::invalid_argument(G_NPRED, msg);
            }

            // Get CTA event list
            const GCTAEventList* events = dynamic_cast<const GCTAEventList*>(obs.events());
            if (events == NULL) {
                std::string msg = "No CTA event list found in observation.\n" +
                                  obs.print();
                throw GException::invalid_argument(G_NPRED, msg);
            }

            // Get reference to ROI centre
            const GSkyDir& roi_centre = events->roi().centre().dir();

            // Get ROI radius in radians
            double roi_radius = events->roi().radius() * gammalib::deg2rad;

            // Get log10 of energy in TeV
            double logE = obsEng.log10TeV();

            // Setup integration function
            GCTAModelIrfBackground::npred_roi_kern_theta integrand(bgd, logE);

            // Setup integrator
            GIntegral integral(&integrand);
            integral.eps(g_cta_inst_background_npred_theta_eps);

            // Spatially integrate radial component
            npred = integral.romberg(0.0, roi_radius);

            // Store result in Npred cache
#if defined(G_USE_NPRED_CACHE)
            m_npred_names.push_back(id);
            m_npred_energies.push_back(obsEng);
            m_npred_times.push_back(obsTime);
            m_npred_values.push_back(npred);
#endif

            // Debug: Check for NaN
#if defined(G_NAN_CHECK)
            if (gammalib::is_notanumber(npred) || gammalib::is_infinite(npred)) {
                std::string origin  = "GCTAModelIrfBackground::npred";
                std::string message = " NaN/Inf encountered (npred=" +
                                      gammalib::str(npred) + ", roi_radius=" +
                                      gammalib::str(roi_radius) + ")";
                gammalib::warning(origin, message);
            }
#endif

        } // endif: model was valid

    } // endif: Npred computation required

    // Multiply in spectral and temporal components
    npred *= spectral()->eval(obsEng, obsTime);
    npred *= temporal()->eval(obsTime);

    // Apply deadtime correction
    npred *= obs.deadc(obsTime);

    // Return Npred
    return npred;
}