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); }
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); }
/***********************************************************************//** * @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; }
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); }
/***********************************************************************//** * @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; }
/***********************************************************************//** * @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; }