/***********************************************************************//** * @brief Sum effective area multiplied by livetime over zenith and * (optionally) azimuth angles * * @param[in] dir True sky direction. * @param[in] energy True photon energy. * @param[in] aeff Effective area. * * Computes * \f[\sum_{\cos \theta, \phi} T_{\rm live}(\cos \theta, \phi) * A_{\rm eff}(\log E, \cos \theta, \phi)\f] * where * \f$T_{\rm live}(\cos \theta, \phi)\f$ is the livetime as a function of * the cosine of the zenith and the azimuth angle, and * \f$A_{\rm eff}(\log E, \cos \theta, \phi)\f$ is the effective area that * depends on * the log10 of the energy (in MeV), * the cosine of the zenith angle, and * the azimuth angle. * This method assumes that \f$T_{\rm live}(\cos \theta, \phi)\f$ is * stored as a set of maps in a 2D array with \f$\cos \theta\f$ being the * most rapidely varying parameter and with the first map starting at * index m_num_ctheta (the first m_num_ctheta maps are the livetime cube * maps without any \f$\phi\f$ dependence). ***************************************************************************/ double GLATLtCubeMap::operator()(const GSkyDir& dir, const GEnergy& energy, const GLATAeff& aeff) const { // Get map index int pixel = m_map.dir2pix(dir); // Initialise sum double sum = 0.0; // Circumvent const correctness GLATAeff* fct = ((GLATAeff*)&aeff); // If livetime cube and response have phi dependence then sum over // zenith and azimuth. Note that the map index starts with m_num_ctheta // as the first m_num_ctheta maps correspond to an evaluation without // any phi-dependence. if (has_phi() && aeff.has_phi()) { for (int iphi = 0, i = m_num_ctheta; iphi < m_num_phi; ++iphi) { double p = phi(iphi); for (int itheta = 0; itheta < m_num_ctheta; ++itheta, ++i) { sum += m_map(pixel, i) * (*fct)(energy.log10MeV(), costheta(i), p); } } } // ... otherwise sum only over zenith angle else { for (int i = 0; i < m_num_ctheta; ++i) { sum += m_map(pixel, i) * (*fct)(energy.log10MeV(), costheta(i)); } } // Return sum return sum; }
///< The function projects latitude-longitude environment map to SH basis up to lmax band void ShProjectEnvironmentMap(float3 const* envmap, int width, int height, int lmax, float3* coeffs) { // Resulting coefficients for RGB // std::vector<float3> coeffs(NumShTerms(lmax)); // Temporary coefficients storage std::vector<float> ylm(NumShTerms(lmax)); // Precompute sin and cos for the sphere std::vector<float> sintheta(height); std::vector<float> costheta(height); std::vector<float> sinphi(width); std::vector<float> cosphi(width); float thetastep = PI / height; float phistep = 2.f * PI / width; float theta0 = PI / height / 2; float phi0 = 2.f * PI / width / 2; for (int i = 0; i < width; ++i) { sinphi[i] = std::sin(phi0 + i * phistep); cosphi[i] = std::cos(phi0 + i * phistep); } for (int i = 0; i < height; ++i) { sintheta[i] = std::sin(theta0 + i * thetastep); costheta[i] = std::cos(theta0 + i * thetastep); } // Iterate over the pixels calculating Riemann sum for (int phi = 0; phi < width; ++phi) { for (int theta = 0; theta < height; ++theta) { // Construct direction vector float3 w = normalize(float3(sintheta[theta] * cosphi[phi], costheta[theta], sintheta[theta] * sinphi[phi])); // Construct uv sample coordinates float2 uv = float2((float)phi / width, (float)theta / height); // Sample environment map int iu = (int)floor(uv.x * width); int iv = (int)floor(uv.y * height); float3 le = envmap[width * iv + iu]; // Evaluate SH functions at w up to lmax band ShEvaluate(w, lmax, &ylm[0]); // Evaluate Riemann sum accouting for solid angle conversion (sin term) for (int i = 0; i < NumShTerms(lmax); ++i) { coeffs[i] += le * ylm[i] * sintheta[theta] * (PI / height) * (2.f * PI / width); } } } }
///< The function evaluates SH functions and dumps values to latitude-longitude map void ShEvaluateAndDump(ImageIo& io, std::string const& filename, int width, int height, int lmax, float3 const* coeffs) { // Prepare image memory std::vector<float> imagedata(width * height * 3); // Allocate space for SH functions std::vector<float> ylm(NumShTerms(lmax)); // Precalculate sin and cos terms std::vector<float> sintheta(height); std::vector<float> costheta(height); std::vector<float> sinphi(width); std::vector<float> cosphi(width); float thetastep = PI / height; float phistep = 2.f*PI / width; float theta0 = PI / height / 2; float phi0= 2.f*PI / width / 2; for (int i = 0; i < width; ++i) { sinphi[i] = std::sin(phi0 + i * phistep); cosphi[i] = std::cos(phi0 + i * phistep); } for (int i = 0; i < height; ++i) { sintheta[i] = std::sin(theta0 + i * thetastep); costheta[i] = std::cos(theta0 + i * thetastep); } // Iterate thru image pixels for (int phi = 0; phi < width; ++phi) { for (int theta = 0; theta < height; ++theta) { // Calculate direction float3 w = normalize(float3(sintheta[theta] * cosphi[phi], costheta[theta], sintheta[theta] * sinphi[phi])); // Evaluate SH functions at w up to lmax band ShEvaluate(w, lmax, &ylm[0]); // Evaluate function injecting SH coeffs for (int i = 0; i < NumShTerms(lmax); ++i) { imagedata[theta * width * 3 + phi * 3] += ylm[i] * coeffs[i].x; imagedata[theta * width * 3 + phi * 3 + 1] += ylm[i] * coeffs[i].y; imagedata[theta * width * 3 + phi * 3 + 2] += ylm[i] * coeffs[i].z; } } } // Write image to file ImageIo::ImageDesc imgdesc(width, height, 3); io.Write(filename, imagedata, imgdesc); }
/***********************************************************************//** * @brief Sum function multiplied by livetime over zenith angle * * @param[in] dir Sky direction. * @param[in] fct Function to evaluate. * * Computes * \f[\sum_{\cos \theta} T_{\rm live}(\cos \theta) f(\cos \theta)\f] * where * \f$T_{\rm live}(\cos \theta)\f$ is the livetime as a function of the * cosine of the zenith angle, and * \f$f(\cos \theta)\f$ is a function that depends on the cosine of the * zenith angle. * This method assumes that \f$T_{\rm live}(\cos \theta)\f$ is stored as a * set of maps. ***************************************************************************/ double GLATLtCubeMap::operator()(const GSkyDir& dir, _ltcube_ctheta fct) const { // Get map index int pixel = m_map.dir2pix(dir); // Initialise sum double sum = 0.0; // Loop over zenith angles for (int i = 0; i < m_num_ctheta; ++i) { sum += m_map(pixel, i) * (*fct)(costheta(i)); } // Return sum return sum; }
/***********************************************************************//** * @brief Sum function multiplied by livetime over zenith and azimuth angles * * @param[in] dir Sky direction. * @param[in] fct Function to evaluate. * * Computes * \f[\sum_{\cos \theta, \phi} T_{\rm live}(\cos \theta, \phi) * f(\cos \theta, \phi)\f] * where * \f$T_{\rm live}(\cos \theta, \phi)\f$ is the livetime as a function of * the cosine of the zenith and of the azimuth angle, and * \f$f(\cos \theta, \phi)\f$ is a function that depends on the cosine of * the zenith angle and of the azimuth angle. * This method assumes that \f$T_{\rm live}(\cos \theta, \phi)\f$ is * stored as a set of maps in a 2D array with \f$\cos \theta\f$ being the * most rapidely varying parameter and with the first map starting at * index m_num_ctheta (the first m_num_ctheta maps are the livetime cube * maps without any \f$\phi\f$ dependence). ***************************************************************************/ double GLATLtCubeMap::operator()(const GSkyDir& dir, _ltcube_ctheta_phi fct) const { // Get map index int pixel = m_map.dir2pix(dir); // Initialise sum double sum = 0.0; // Loop over azimuth and zenith angles. Note that the map index starts // with m_num_ctheta as the first m_num_ctheta maps correspond to an // evaluation without any phi-dependence. for (int iphi = 0, i = m_num_ctheta; iphi < m_num_phi; ++iphi) { double p = phi(iphi); for (int itheta = 0; itheta < m_num_ctheta; ++itheta, ++i) { sum += m_map(pixel, i) * (*fct)(costheta(itheta), p); } } // Return sum return sum; }
///< The function projects latitude-longitude environment map to SH basis up to lmax band void ShProjectEnvironmentMap(TextureSystem const& texsys, std::string const& texture, int lmax, float3* coeffs) { // Resulting coefficients for RGB // std::vector<float3> coeffs(NumShTerms(lmax)); // Temporary coefficients storage std::vector<float> ylm(NumShTerms(lmax)); // Get texture width and height TextureSystem::TextureDesc texdesc; texsys.GetTextureInfo(texture, texdesc); // Precompute sin and cos for the sphere std::vector<float> sintheta(texdesc.height); std::vector<float> costheta(texdesc.height); std::vector<float> sinphi(texdesc.width); std::vector<float> cosphi(texdesc.width); float thetastep = PI / texdesc.height; float phistep = 2.f*PI / texdesc.width; float theta0 = PI / texdesc.height / 2; float phi0 = 2.f*PI / texdesc.width / 2; for (int i = 0; i < texdesc.width; ++i) { sinphi[i] = std::sin(phi0 + i * phistep); cosphi[i] = std::cos(phi0 + i * phistep); } for (int i = 0; i < texdesc.height; ++i) { sintheta[i] = std::sin(theta0 + i * thetastep); costheta[i] = std::cos(theta0 + i * thetastep); } // Iterate over the pixels calculating Riemann sum for (int phi = 0; phi < texdesc.width; ++phi) { for (int theta = 0; theta < texdesc.height; ++theta) { // Construct direction vector float3 w = normalize(float3(sintheta[theta] * cosphi[phi], costheta[theta], sintheta[theta] * sinphi[phi])); // Construct uv sample coordinates float2 uv = float2((float)phi / texdesc.width, (float)theta / texdesc.height); // Sample environment map TextureSystem::Options opts; opts.wrapmode = TextureSystem::Options::kRepeat; opts.filter= TextureSystem::Options::kPoint; float3 le = texsys.Sample(texture, uv, float2()); // Evaluate SH functions at w up to lmax band ShEvaluate(w, lmax, &ylm[0]); // Evaluate Riemann sum accouting for solid angle conversion (sin term) for (int i = 0; i < NumShTerms(lmax); ++i) { coeffs[i] += le * ylm[i] * sintheta[theta] * (PI / texdesc.height) * (2.f * PI / texdesc.width); } } } }