/***********************************************************************//** * @brief Evaluate function * * @param[in] photon Incident photon. * @return Model value. * * Computes the spatial diffuse model as function of photon parameters. ***************************************************************************/ double GModelSpatialDiffuseCube::eval(const GPhoton& photon) const { // Initialise value double value = 0.0; // Fetch cube fetch_cube(); // Continue only if there is energy information for the map cube if (m_logE.size() > 0) { // Compute diffuse model value by interpolation in log10(energy) m_logE.set_value(photon.energy().log10MeV()); double intensity = m_logE.wgt_left() * m_cube(photon.dir(), m_logE.inx_left()) + m_logE.wgt_right() * m_cube(photon.dir(), m_logE.inx_right()); // Set the intensity times the scaling factor as model value value = intensity * m_value.value(); // Make sure that value is not negative if (value < 0.0) { value = 0.0; } } // endif: energy information was available // Return value return value; }
/***********************************************************************//** * @brief Return exposure (in units of cm2 s) * * @param[in] dir Coordinate of the true photon position. * @param[in] energy Energy of the true photon. * @return Exposure (in units of cm2 s) ***************************************************************************/ double GCTACubeExposure::operator()(const GSkyDir& dir, const GEnergy& energy) const { // Set indices and weighting factors for interpolation update(energy.log10TeV()); // Perform interpolation double exposure = m_wgt_left * m_cube(dir, m_inx_left) + m_wgt_right * m_cube(dir, m_inx_right); // Make sure that exposure does not become negative if (exposure < 0.0) { exposure = 0.0; } // Return exposure return exposure; }
/***********************************************************************//** * @brief Set exposure cube from one CTA observation * * @param[in] obs CTA observation. * * Set the exposure cube from a single CTA observations. The cube pixel * values are computed as product of the effective area and the livetime. * * @todo: Throw an exception if response is not valid ***************************************************************************/ void GCTACubeExposure::set(const GCTAObservation& obs) { // Clear GTIs, reset livetime and exposure cube pixels m_gti.clear(); m_livetime = 0.0; m_cube = 0.0; // Extract region of interest from CTA observation GCTARoi roi = obs.roi(); // Get references on CTA response and pointing direction const GCTAResponseIrf* rsp = dynamic_cast<const GCTAResponseIrf*>(obs.response()); const GSkyDir& pnt = obs.pointing().dir(); // Continue only if response is valid if (rsp != NULL) { // Loop over all pixels in sky map for (int pixel = 0; pixel < m_cube.npix(); ++pixel) { // Get pixel sky direction GSkyDir dir = m_cube.inx2dir(pixel); // Continue only if pixel is within RoI if (roi.centre().dir().dist_deg(dir) <= roi.radius()) { // Compute theta angle with respect to pointing direction // in radians double theta = pnt.dist(dir); // Loop over all exposure cube energy bins for (int iebin = 0; iebin < m_ebounds.size(); ++iebin){ // Get logE/TeV double logE = m_ebounds.elogmean(iebin).log10TeV(); // Set exposure cube (effective area * lifetime) m_cube(pixel, iebin) = rsp->aeff(theta, 0.0, 0.0, 0.0, logE) * obs.livetime(); } // endfor: looped over energy bins } // endif: pixel was within RoI } // endfor: looped over all pixels // Append GTIs and increment livetime m_gti.extend(obs.gti()); m_livetime += obs.livetime(); } // endif: response was valid // Return return; }
/***********************************************************************//** * @brief Evaluate function and gradients * * @param[in] photon Incident photon. * @return Model value. * * Computes the spatial diffuse model as function of photon parameters and * sets the value gradient. ***************************************************************************/ double GModelSpatialDiffuseCube::eval_gradients(const GPhoton& photon) const { // Initialise intensity double intensity = 0.0; // Fetch cube fetch_cube(); // Continue only if there is energy information for the map cube if (m_logE.size() > 0) { // Compute diffuse model value by interpolation in log10(energy) m_logE.set_value(photon.energy().log10MeV()); intensity = m_logE.wgt_left() * m_cube(photon.dir(), m_logE.inx_left()) + m_logE.wgt_right() * m_cube(photon.dir(), m_logE.inx_right()); } // endif: energy information was available // Compute the model value double value = intensity * m_value.value(); // Compute partial derivatives of the parameter value double g_value = (m_value.is_free()) ? intensity * m_value.scale() : 0.0; // Make sure that value is not negative if (value < 0.0) { value = 0.0; g_value = 0.0; } // Set gradient to 0 (circumvent const correctness) const_cast<GModelSpatialDiffuseCube*>(this)->m_value.factor_gradient(g_value); // Return value return value; }
/***********************************************************************//** * @brief Fill events into counts cube * * @param[in] obs CTA observation. * * @exception GException::invalid_value * No event list found in observation. * * Fills the events from an event list in the counts cube setup by init_cube. ***************************************************************************/ void ctbin::fill_cube(GCTAObservation* obs) { // Continue only if observation pointer is valid if (obs != NULL) { // Make sure that the observation holds a CTA event list. If this // is not the case then throw an exception. const GCTAEventList* events = dynamic_cast<const GCTAEventList*>(obs->events()); if (events == NULL) { std::string msg = "CTA Observation does not contain an event " "list. Event list information is needed to " "fill the counts map."; throw GException::invalid_value(G_FILL_CUBE, msg); } // Get the RoI const GCTARoi& roi = events->roi(); // Initialise binning statistics int num_outside_roi = 0; int num_outside_map = 0; int num_outside_ebds = 0; int num_in_map = 0; // Fill sky map for (int i = 0; i < events->size(); ++i) { // Get event const GCTAEventAtom* event = (*events)[i]; // Determine sky pixel GCTAInstDir* inst = (GCTAInstDir*)&(event->dir()); GSkyDir dir = inst->dir(); GSkyPixel pixel = m_cube.dir2pix(dir); // Skip if pixel is outside RoI if (roi.centre().dir().dist_deg(dir) > roi.radius()) { num_outside_roi++; continue; } // Skip if pixel is out of range if (pixel.x() < -0.5 || pixel.x() > (m_cube.nx()-0.5) || pixel.y() < -0.5 || pixel.y() > (m_cube.ny()-0.5)) { num_outside_map++; continue; } // Determine energy bin. Skip if we are outside the energy range int index = m_ebounds.index(event->energy()); if (index == -1) { num_outside_ebds++; continue; } // Fill event in skymap m_cube(pixel, index) += 1.0; num_in_map++; } // endfor: looped over all events // Append GTIs m_gti.extend(events->gti()); // Update ontime and livetime m_ontime += obs->ontime(); m_livetime += obs->livetime(); // Log filling results if (logTerse()) { log << gammalib::parformat("Events in list"); log << obs->events()->size() << std::endl; log << gammalib::parformat("Events in cube"); log << num_in_map << std::endl; log << gammalib::parformat("Event bins outside RoI"); log << num_outside_roi << std::endl; log << gammalib::parformat("Events outside cube area"); log << num_outside_map << std::endl; log << gammalib::parformat("Events outside energy bins"); log << num_outside_ebds << std::endl; } // Log cube if (logExplicit()) { log.header1("Counts cube"); log << m_cube << std::endl; } } // endif: observation was valid // Return return; }
/***********************************************************************//** * @brief Set Monte Carlo simulation cone * * @param[in] centre Simulation cone centre. * @param[in] radius Simulation cone radius (degrees). * * Sets the simulation cone centre and radius that defines the directions * that will be simulated using the mc() method. ***************************************************************************/ void GModelSpatialDiffuseCube::set_mc_cone(const GSkyDir& centre, const double& radius) { // Initialise cache m_mc_cache.clear(); m_mc_spectrum.clear(); // Fetch cube fetch_cube(); // Determine number of cube pixels and maps int npix = pixels(); int nmaps = maps(); // Continue only if there are pixels and maps if (npix > 0 && nmaps > 0) { // Reserve space for all pixels in cache m_mc_cache.reserve((npix+1)*nmaps); // Loop over all maps for (int i = 0; i < nmaps; ++i) { // Compute pixel offset int offset = i * (npix+1); // Set first cache value to 0 m_mc_cache.push_back(0.0); // Initialise cache with cumulative pixel fluxes and compute // total flux in skymap for normalization. Negative pixels are // excluded from the cumulative map. double total_flux = 0.0; for (int k = 0; k < npix; ++k) { // Derive effective pixel radius from half opening angle // that corresponds to the pixel's solid angle. For security, // the radius is enhanced by 50%. double pixel_radius = std::acos(1.0 - m_cube.solidangle(k)/gammalib::twopi) * gammalib::rad2deg * 1.5; // Add up flux with simulation cone radius + effective pixel // radius. The effective pixel radius is added to make sure // that all pixels that overlap with the simulation cone are // taken into account. There is no problem of having even // pixels outside the simulation cone taken into account as // long as the mc() method has an explicit test of whether a // simulated event is contained in the simulation cone. double distance = centre.dist_deg(m_cube.pix2dir(k)); if (distance <= radius+pixel_radius) { double flux = m_cube(k,i) * m_cube.solidangle(k); if (flux > 0.0) { total_flux += flux; } } // Push back flux m_mc_cache.push_back(total_flux); // units: ph/cm2/s/MeV } // Normalize cumulative pixel fluxes so that the values in the // cache run from 0 to 1 if (total_flux > 0.0) { for (int k = 0; k < npix; ++k) { m_mc_cache[k+offset] /= total_flux; } } // Make sure that last pixel in the cache is >1 m_mc_cache[npix+offset] = 1.0001; // Store centre flux in node array if (m_logE.size() == nmaps) { GEnergy energy; energy.log10MeV(m_logE[i]); // Only append node if flux > 0 if (total_flux > 0.0) { m_mc_spectrum.append(energy, total_flux); } } } // endfor: looped over all maps // Dump cache values for debugging #if defined(G_DEBUG_CACHE) for (int i = 0; i < m_mc_cache.size(); ++i) { std::cout << "i=" << i; std::cout << " c=" << m_mc_cache[i] << std::endl; } #endif } // endif: there were cube pixels and maps // Return return; }