/***********************************************************************//** * @brief Test CTA Npred computation * * Tests the Npred computation for the diffuse source model. This is done * by loading the model from the XML file and by calling the * GCTAObservation::npred method which in turn calls the * GCTAResponse::npred_diffuse method. The test takes a few seconds. ***************************************************************************/ void TestGCTAResponse::test_response_npred_diffuse(void) { // Set reference value double ref = 11212.26274; // Set parameters double src_ra = 201.3651; double src_dec = -43.0191; double roi_rad = 4.0; // Setup ROI centred on Cen A with a radius of 4 deg GCTARoi roi; GCTAInstDir instDir; instDir.radec_deg(src_ra, src_dec); roi.centre(instDir); roi.radius(roi_rad); // Setup pointing on Cen A GSkyDir skyDir; skyDir.radec_deg(src_ra, src_dec); GCTAPointing pnt; pnt.dir(skyDir); // Setup dummy event list GGti gti; GEbounds ebounds; GTime tstart(0.0); GTime tstop(1800.0); GEnergy emin; GEnergy emax; emin.TeV(0.1); emax.TeV(100.0); gti.append(tstart, tstop); ebounds.append(emin, emax); GCTAEventList events; events.roi(roi); events.gti(gti); events.ebounds(ebounds); // Setup dummy CTA observation GCTAObservation obs; obs.ontime(1800.0); obs.livetime(1600.0); obs.deadc(1600.0/1800.0); obs.response(cta_irf, cta_caldb); obs.events(&events); obs.pointing(pnt); // Load models for Npred computation GModels models(cta_rsp_xml); // Perform Npred computation double npred = obs.npred(models, NULL); // Test Npred test_value(npred, ref, 1.0e-5, "Diffuse Npred computation"); // Return return; }
/***********************************************************************//** * @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 Simulate source events from photon list * * @param[in] obs Pointer on CTA observation. * @param[in] models Model list. * @param[in] ran Random number generator. * @param[in] wrklog Pointer to logger. * * Simulate source events from a photon list for a given CTA observation. * The events are stored in as event list in the observation. * * This method does nothing if the observation pointer is NULL. It also * verifies if the observation has a valid pointing and response. ***************************************************************************/ void ctobssim::simulate_source(GCTAObservation* obs, const GModels& models, GRan& ran, GLog* wrklog) { // Continue only if observation pointer is valid if (obs != NULL) { // If no logger is specified then use the default logger if (wrklog == NULL) { wrklog = &log; } // Get CTA response const GCTAResponseIrf* rsp = dynamic_cast<const GCTAResponseIrf*>(obs->response()); if (rsp == NULL) { std::string msg = "Response is not an IRF response.\n" + obs->response()->print(); throw GException::invalid_value(G_SIMULATE_SOURCE, msg); } // Get pointer on event list (circumvent const correctness) GCTAEventList* events = static_cast<GCTAEventList*>(const_cast<GEvents*>(obs->events())); // Extract simulation region. GSkyDir dir = events->roi().centre().dir(); double rad = events->roi().radius() + g_roi_margin; // Dump simulation cone information if (logNormal()) { *wrklog << gammalib::parformat("Simulation area"); *wrklog << m_area << " cm2" << std::endl; *wrklog << gammalib::parformat("Simulation cone"); *wrklog << "RA=" << dir.ra_deg() << " deg"; *wrklog << ", Dec=" << dir.dec_deg() << " deg"; *wrklog << ", r=" << rad << " deg" << std::endl; } // Initialise indentation for logging int indent = 0; // Loop over all Good Time Intervals for (int it = 0; it < events->gti().size(); ++it) { // Extract time interval GTime tmin = events->gti().tstart(it); GTime tmax = events->gti().tstop(it); // Dump time interval if (logNormal()) { if (events->gti().size() > 1) { indent++; wrklog->indent(indent); } *wrklog << gammalib::parformat("Time interval", indent); *wrklog << tmin.convert(m_cta_ref); *wrklog << " - "; *wrklog << tmax.convert(m_cta_ref); *wrklog << " s" << std::endl; } // Loop over all energy boundaries for (int ie = 0; ie < events->ebounds().size(); ++ie) { // Extract energy boundaries GEnergy emin = events->ebounds().emin(ie); GEnergy emax = events->ebounds().emax(ie); // Set true photon energy limits for simulation. If observation // has energy dispersion then add margin GEnergy e_true_min = emin; GEnergy e_true_max = emax; if (rsp->use_edisp()) { e_true_min = rsp->ebounds(e_true_min).emin(); e_true_max = rsp->ebounds(e_true_max).emax(); } // Dump energy range if (logNormal()) { if (events->ebounds().size() > 1) { indent++; wrklog->indent(indent); } *wrklog << gammalib::parformat("Photon energy range", indent); *wrklog << e_true_min << " - " << e_true_max << std::endl; *wrklog << gammalib::parformat("Event energy range", indent); *wrklog << emin << " - " << emax << std::endl; } // Loop over all sky models for (int i = 0; i < models.size(); ++i) { // Get sky model (NULL if not a sky model) const GModelSky* model = dynamic_cast<const GModelSky*>(models[i]); // If we have a sky model that applies to the present // observation then simulate events if (model != NULL && model->is_valid(obs->instrument(), obs->id())) { // Determine duration of a time slice by limiting the // number of simulated photons to m_max_photons. // The photon rate is estimated from the model flux // and used to set the duration of the time slice. double flux = get_model_flux(model, emin, emax, dir, rad); double rate = flux * m_area; double duration = 1800.0; // default: 1800 sec if (rate > 0.0) { duration = m_max_photons / rate; if (duration < 1.0) { // not <1 sec duration = 1.0; } else if (duration > 180000.0) { // not >50 hr duration = 180000.0; } } GTime tslice(duration, "sec"); // If photon rate exceeds the maximum photon rate // then throw an exception if (rate > m_max_rate) { std::string modnam = (model->name().length() > 0) ? model->name() : "Unknown"; std::string msg = "Photon rate "+ gammalib::str(rate)+ " photons/sec for model \""+ modnam+"\" exceeds maximum" " allowed photon rate of "+ gammalib::str(m_max_rate)+ " photons/sec. Please check" " the model parameters for" " model \""+modnam+"\" or" " increase the value of the" " hidden \"maxrate\"" " parameter."; throw GException::invalid_value(G_SIMULATE_SOURCE, msg); } // Dump length of time slice and rate if (logExplicit()) { *wrklog << gammalib::parformat("Photon rate", indent); *wrklog << rate << " photons/sec"; if (model->name().length() > 0) { *wrklog << " [" << model->name() << "]"; } *wrklog << std::endl; } // To reduce memory requirements we split long time // intervals into several slices. GTime tstart = tmin; GTime tstop = tstart + tslice; // Initialise cumulative photon counters int nphotons = 0; // Loop over time slices while (tstart < tmax) { // Make sure that tstop <= tmax if (tstop > tmax) { tstop = tmax; } // Dump time slice if (logExplicit()) { if (tmax - tmin > tslice) { indent++; wrklog->indent(indent); } *wrklog << gammalib::parformat("Time slice", indent); *wrklog << tstart.convert(m_cta_ref); *wrklog << " - "; *wrklog << tstop.convert(m_cta_ref); *wrklog << " s"; if (model->name().length() > 0) { *wrklog << " [" << model->name() << "]"; } *wrklog << std::endl; } // Get photons GPhotons photons = model->mc(m_area, dir, rad, e_true_min, e_true_max, tstart, tstop, ran); // Dump number of simulated photons if (logExplicit()) { *wrklog << gammalib::parformat("MC source photons/slice", indent); *wrklog << photons.size(); if (model->name().length() > 0) { *wrklog << " [" << model->name() << "]"; } *wrklog << std::endl; } // Simulate events from photons for (int i = 0; i < photons.size(); ++i) { // Increment photon counter nphotons++; // Simulate event. Note that this method // includes the deadtime correction. GCTAEventAtom* event = rsp->mc(m_area, photons[i], *obs, ran); if (event != NULL) { // Use event only if it falls within ROI // energy boundary and time slice if (events->roi().contains(*event) && event->energy() >= emin && event->energy() <= emax && event->time() >= tstart && event->time() <= tstop) { event->event_id(m_event_id); events->append(*event); m_event_id++; } delete event; } } // endfor: looped over events // Go to next time slice tstart = tstop; tstop = tstart + tslice; // Reset indentation if (logExplicit()) { if (tmax - tmin > tslice) { indent--; wrklog->indent(indent); } } } // endwhile: looped over time slices // Dump simulation results if (logNormal()) { *wrklog << gammalib::parformat("MC source photons", indent); *wrklog << nphotons; if (model->name().length() > 0) { *wrklog << " [" << model->name() << "]"; } *wrklog << std::endl; *wrklog << gammalib::parformat("MC source events", indent); *wrklog << events->size(); if (model->name().length() > 0) { *wrklog << " [" << model->name() << "]"; } *wrklog << std::endl; } } // endif: model was a sky model } // endfor: looped over models // Dump simulation results if (logNormal()) { *wrklog << gammalib::parformat("MC source events", indent); *wrklog << events->size(); *wrklog << " (all source models)"; *wrklog << std::endl; } // Reset indentation if (logNormal()) { if (events->ebounds().size() > 1) { indent--; wrklog->indent(indent); } } } // endfor: looped over all energy boundaries // Reset indentation if (logNormal()) { if (events->gti().size() > 1) { indent--; wrklog->indent(indent); } } } // endfor: looped over all time intervals // Reset indentation wrklog->indent(0); } // endif: observation pointer was valid // Return return; }
/***********************************************************************//** * @brief Simulate source events from photon list * * @param[in] obs Pointer on CTA observation. * @param[in] models Model list. * @param[in] ran Random number generator. * @param[in] wrklog Pointer to logger. * * Simulate source events from a photon list for a given CTA observation. * The events are stored in as event list in the observation. * * This method does nothing if the observation pointer is NULL. It also * verifies if the observation has a valid pointing and response. ***************************************************************************/ void ctobssim::simulate_source(GCTAObservation* obs, const GModels& models, GRan& ran, GLog* wrklog) { // Continue only if observation pointer is valid if (obs != NULL) { // If no logger is specified then use the default logger if (wrklog == NULL) { wrklog = &log; } // Get pointer on CTA response. Throw an exception if the response // is not defined. const GCTAResponse& rsp = obs->response(); // Make sure that the observation holds a CTA event list. If this // is not the case then allocate and attach a CTA event list now. if (dynamic_cast<const GCTAEventList*>(obs->events()) == NULL) { set_list(obs); } // Get pointer on event list (circumvent const correctness) GCTAEventList* events = static_cast<GCTAEventList*>(const_cast<GEvents*>(obs->events())); // Extract simulation region. GSkyDir dir = events->roi().centre().dir(); double rad = events->roi().radius() + g_roi_margin; // Dump simulation cone information if (logNormal()) { *wrklog << gammalib::parformat("Simulation area"); *wrklog << m_area << " cm2" << std::endl; *wrklog << gammalib::parformat("Simulation cone"); *wrklog << "RA=" << dir.ra_deg() << " deg"; *wrklog << ", Dec=" << dir.dec_deg() << " deg"; *wrklog << ", r=" << rad << " deg" << std::endl; } // Initialise indentation for logging int indent = 0; // Loop over all Good Time Intervals for (int it = 0; it < events->gti().size(); ++it) { // Extract time interval GTime tmin = events->gti().tstart(it); GTime tmax = events->gti().tstop(it); // Dump time interval if (logNormal()) { if (events->gti().size() > 1) { indent++; wrklog->indent(indent); } *wrklog << gammalib::parformat("Time interval", indent); *wrklog << tmin.convert(m_cta_ref); *wrklog << " - "; *wrklog << tmax.convert(m_cta_ref); *wrklog << " s" << std::endl; } // Loop over all energy boundaries for (int ie = 0; ie < events->ebounds().size(); ++ie) { // Extract energy boundaries GEnergy emin = events->ebounds().emin(ie); GEnergy emax = events->ebounds().emax(ie); // Dump energy range if (logNormal()) { if (events->ebounds().size() > 1) { indent++; wrklog->indent(indent); } *wrklog << gammalib::parformat("Energy range", indent); *wrklog << emin << " - " << emax << std::endl; } // Loop over all sky models for (int i = 0; i < models.size(); ++i) { // Get sky model (NULL if not a sky model) const GModelSky* model = dynamic_cast<const GModelSky*>(models[i]); // If we have a sky model that applies to the present // observation then simulate events if (model != NULL && model->is_valid(obs->instrument(), obs->id())) { // Determine duration of a time slice by limiting the // number of simulated photons to m_max_photons. // The photon rate is estimated from the model flux // and used to set the duration of the time slice. double flux = model->spectral()->flux(emin, emax); double rate = flux * m_area; double duration = 1800.0; // default: 1800 sec if (rate > 0.0) { duration = m_max_photons / rate; if (duration < 1.0) { // not <1 sec duration = 1.0; } else if (duration > 180000.0) { // not >50 hr duration = 180000.0; } } GTime tslice(duration, "sec"); // To reduce memory requirements we split long time // intervals into several slices. GTime tstart = tmin; GTime tstop = tstart + tslice; // Initialise cumulative photon counters int nphotons = 0; // Loop over time slices while (tstart < tmax) { // Make sure that tstop <= tmax if (tstop > tmax) { tstop = tmax; } // Dump time slice if (logExplicit()) { if (tmax - tmin > tslice) { indent++; wrklog->indent(indent); } *wrklog << gammalib::parformat("Time slice", indent); *wrklog << tstart.convert(m_cta_ref); *wrklog << " - "; *wrklog << tstop.convert(m_cta_ref); *wrklog << " s" << std::endl; } // Get photons GPhotons photons = model->mc(m_area, dir, rad, emin, emax, tstart, tstop, ran); // Dump number of simulated photons if (logExplicit()) { *wrklog << gammalib::parformat("MC source photons/slice", indent); *wrklog << photons.size(); if (model->name().length() > 0) { *wrklog << " [" << model->name() << "]"; } *wrklog << std::endl; } // Simulate events from photons for (int i = 0; i < photons.size(); ++i) { // Increment photon counter nphotons++; // Simulate event. Note that this method // includes the deadtime correction. GCTAEventAtom* event = rsp.mc(m_area, photons[i], *obs, ran); if (event != NULL) { // Use event only if it falls within ROI if (events->roi().contains(*event)) { event->event_id(m_event_id); events->append(*event); m_event_id++; } delete event; } } // endfor: looped over events // Go to next time slice tstart = tstop; tstop = tstart + tslice; // Reset indentation if (logExplicit()) { if (tmax - tmin > tslice) { indent--; wrklog->indent(indent); } } } // endwhile: looped over time slices // Dump simulation results if (logNormal()) { *wrklog << gammalib::parformat("MC source photons", indent); *wrklog << nphotons; if (model->name().length() > 0) { *wrklog << " [" << model->name() << "]"; } *wrklog << std::endl; *wrklog << gammalib::parformat("MC source events", indent); *wrklog << events->size(); if (model->name().length() > 0) { *wrklog << " [" << model->name() << "]"; } *wrklog << std::endl; } } // endif: model was a sky model } // endfor: looped over models // Dump simulation results if (logNormal()) { *wrklog << gammalib::parformat("MC source events", indent); *wrklog << events->size(); *wrklog << " (all source models)"; *wrklog << std::endl; } // Reset indentation if (logNormal()) { if (events->ebounds().size() > 1) { indent--; wrklog->indent(indent); } } } // endfor: looped over all energy boundaries // Reset indentation if (logNormal()) { if (events->gti().size() > 1) { indent--; wrklog->indent(indent); } } } // endfor: looped over all time intervals // Reset indentation wrklog->indent(0); } // endif: observation pointer was valid // Return return; }
/***********************************************************************//** * @brief Set empty CTA event list * * @param[in] obs CTA observation. * * Attaches an empty event list to CTA observation. The method also sets the * pointing direction using the m_ra and m_dec members, the ROI based on * m_ra, m_dec and m_rad, a single GTI based on m_tmin and m_tmax, and a * single energy boundary based on m_emin and m_emax. The method furthermore * sets the ontime, livetime and deadtime correction factor. ***************************************************************************/ void ctobssim::set_list(GCTAObservation* obs) { // Continue only if observation is valid if (obs != NULL) { // Get CTA observation parameters m_ra = (*this)["ra"].real(); m_dec = (*this)["dec"].real(); m_rad = (*this)["rad"].real(); m_tmin = (*this)["tmin"].real(); m_tmax = (*this)["tmax"].real(); m_emin = (*this)["emin"].real(); m_emax = (*this)["emax"].real(); m_deadc = (*this)["deadc"].real(); // Allocate CTA event list GCTAEventList events; // Set pointing direction GCTAPointing pnt; GSkyDir skydir; skydir.radec_deg(m_ra, m_dec); pnt.dir(skydir); // Set ROI GCTAInstDir instdir(skydir); GCTARoi roi(instdir, m_rad); // Set GTI GGti gti(m_cta_ref); GTime tstart; GTime tstop; tstart.set(m_tmin, m_cta_ref); tstop.set(m_tmax, m_cta_ref); gti.append(tstart, tstop); // Set energy boundaries GEbounds ebounds; GEnergy emin; GEnergy emax; emin.TeV(m_emin); emax.TeV(m_emax); ebounds.append(emin, emax); // Set CTA event list attributes events.roi(roi); events.gti(gti); events.ebounds(ebounds); // Attach event list to CTA observation obs->events(events); // Set observation ontime, livetime and deadtime correction factor obs->ontime(gti.ontime()); obs->livetime(gti.ontime()*m_deadc); obs->deadc(m_deadc); } // endif: oberservation was valid // Return return; }
/***********************************************************************//** * @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 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 Select events * * @param[in] obs CTA observation. * @param[in] filename File name. * * Select events from a FITS file by making use of the selection possibility * of the cfitsio library on loading a file. A selection string is created * from the specified criteria that is appended to the filename so that * cfitsio will automatically filter the event data. This selection string * is then applied when opening the FITS file. The opened FITS file is then * saved into a temporary file which is the loaded into the actual CTA * observation, overwriting the old CTA observation. The ROI, GTI and EBounds * of the CTA event list are then set accordingly to the specified selection. * Finally, the temporary file created during this process is removed. * * Good Time Intervals of the observation will be limited to the time * interval [m_tmin, m_tmax]. If m_tmin=m_tmax=0, no time selection is * performed. * * @todo Use INDEF instead of 0.0 for pointing as RA/DEC selection ***************************************************************************/ void ctselect::select_events(GCTAObservation* obs, const std::string& filename) { // Allocate selection string std::string selection; char cmin[80]; char cmax[80]; char cra[80]; char cdec[80]; char crad[80]; // Set requested selections bool select_time = (m_tmin != 0.0 || m_tmax != 0.0); // Set RA/DEC selection double ra = m_ra; double dec = m_dec; if (m_usepnt) { const GCTAPointing *pnt = obs->pointing(); ra = pnt->dir().ra_deg(); dec = pnt->dir().dec_deg(); } // Set time selection interval. We make sure here that the time selection // interval cannot be wider than the GTIs covering the data. This is done // using GGti's reduce() method. if (select_time) { // Reduce GTIs to specified time interval. The complicated cast is // necessary here because the gti() method is declared const, so // we're not officially allowed to modify the GTIs. ((GGti*)(&obs->events()->gti()))->reduce(m_timemin, m_timemax); } // endif: time selection was required // Save GTI for later usage GGti gti = obs->events()->gti(); // Make time selection if (select_time) { // Extract effective time interval in CTA reference time. We need // this reference for filtering. double tmin = gti.tstart().convert(m_cta_ref); double tmax = gti.tstop().convert(m_cta_ref); // Format time with sufficient accuracy and add to selection string sprintf(cmin, "%.8f", tmin); sprintf(cmax, "%.8f", tmax); selection = "TIME >= "+std::string(cmin)+" && TIME <= "+std::string(cmax); if (logTerse()) { log << parformat("Time range"); log << tmin << " - " << tmax << " s" << std::endl; } if (selection.length() > 0) { selection += " && "; } } // Make energy selection sprintf(cmin, "%.8f", m_emin); sprintf(cmax, "%.8f", m_emax); selection += "ENERGY >= "+std::string(cmin)+" && ENERGY <= "+std::string(cmax); if (logTerse()) { log << parformat("Energy range"); log << m_emin << " - " << m_emax << " TeV" << std::endl; } if (selection.length() > 0) { selection += " && "; } // Make ROI selection sprintf(cra, "%.6f", ra); sprintf(cdec, "%.6f", dec); sprintf(crad, "%.6f", m_rad); selection += "ANGSEP("+std::string(cra)+"," + std::string(cdec)+",RA,DEC) <= " + std::string(crad); if (logTerse()) { log << parformat("Acceptance cone centre"); log << "RA=" << ra << ", DEC=" << dec << " deg" << std::endl; log << parformat("Acceptance cone radius"); log << m_rad << " deg" << std::endl; } if (logTerse()) { log << parformat("cfitsio selection"); log << selection << std::endl; } // Add additional expression if (strip_whitespace(m_expr).length() > 0) { if (selection.length() > 0) { selection += " && "; } selection += "("+strip_whitespace(m_expr)+")"; } // Build input filename including selection expression std::string expression = filename; if (selection.length() > 0) expression += "[EVENTS]["+selection+"]"; if (logTerse()) { log << parformat("FITS filename"); log << expression << std::endl; } // Open FITS file GFits file(expression); // Log selected FITS file if (logExplicit()) { log << std::endl; log.header1("FITS file content after selection"); log << file << std::endl; } // Check if we have an EVENTS HDU if (!file.hashdu("EVENTS")) { std::string message = "No \"EVENTS\" extension found in FITS file "+ expression+"."; throw GException::app_error(G_SELECT_EVENTS, message); } // Determine number of events in EVENTS HDU int nevents = file.table("EVENTS")->nrows(); // If the selected event list is empty then append an empty event list // to the observation. Otherwise load the data from the temporary file. if (nevents < 1) { // Create empty event list GCTAEventList eventlist; // Append list to observation obs->events(&eventlist); } else { // Get temporary file name std::string tmpname = std::tmpnam(NULL); // Save FITS file to temporary file file.saveto(tmpname, true); // Load observation from temporary file obs->load_unbinned(tmpname); // Remove temporary file std::remove(tmpname.c_str()); } // Get CTA event list pointer GCTAEventList* list = static_cast<GCTAEventList*>(const_cast<GEvents*>(obs->events())); // Set ROI GCTARoi roi; GCTAInstDir instdir; instdir.radec_deg(ra, dec); roi.centre(instdir); roi.radius(m_rad); list->roi(roi); // Set GTI list->gti(gti); // Set energy boundaries GEbounds ebounds; GEnergy emin; GEnergy emax; emin.TeV(m_emin); emax.TeV(m_emax); ebounds.append(emin, emax); list->ebounds(ebounds); // Recompute ontime and livetime. GTime meantime = 0.5 * (gti.tstart() + gti.tstop()); obs->ontime(gti.ontime()); obs->livetime(gti.ontime() * obs->deadc(meantime)); // Return return; }