Commit 27670925eedeebeb2ee4fe8768f38fa9fd917561
1 parent
e545180c
Implement D1 multi-scatter transmission (#2186)
Showing
3 changed files
with
214 additions
and
5 deletions
inst/com/include/GCOMInstChars.hpp
| ... | ... | @@ -85,6 +85,8 @@ private: |
| 85 | 85 | void read_pos(const GFitsTable& table, std::vector<double>& x, |
| 86 | 86 | std::vector<double>& y); |
| 87 | 87 | void read_selfveto(const GFitsTable& table); |
| 88 | + double ne213a_mfpath(const double& energy) const; | |
| 89 | + double kn_cross_section(const double& x, const double& alpha) const; | |
| 88 | 90 | double min_coeff(const std::vector<double>& coeffs) const; |
| 89 | 91 | double max_coeff(const std::vector<double>& coeffs) const; |
| 90 | 92 | |
| ... | ... | @@ -111,6 +113,8 @@ private: |
| 111 | 113 | std::vector<double> m_d1pos_y; //!< D1 y-position (cm) |
| 112 | 114 | std::vector<double> m_d2pos_x; //!< D2 x-position (cm) |
| 113 | 115 | std::vector<double> m_d2pos_y; //!< D2 y-position (cm) |
| 116 | + GNodeArray m_mfpath_energies; //!< NE213A mean free path energies (MeV) | |
| 117 | + std::vector<double> m_mfpath_coeffs; //!< NE213A mean free path values | |
| 114 | 118 | double m_d1dens; //!< D1 density (g/cm^-3) |
| 115 | 119 | double m_d1rad; //!< D1 radius (cm) |
| 116 | 120 | double m_d1thick; //!< D1 thickness (cm) | ... | ... |
inst/com/src/GCOMIaq.cpp
| ... | ... | @@ -55,7 +55,7 @@ |
| 55 | 55 | //#define G_DEBUG_KLEIN_NISHINA |
| 56 | 56 | //#define G_DEBUG_KLEIN_NISHINA_INTEGRAL |
| 57 | 57 | //#define G_DEBUG_RESPONSE_KERNEL |
| 58 | -//#define G_DEBUG_WEIGHT_IAQ | |
| 58 | +#define G_DEBUG_WEIGHT_IAQ | |
| 59 | 59 | //#define G_DEBUG_SET_CONTINUUM |
| 60 | 60 | //#define G_DEBUG_LOCATION_SMEARING |
| 61 | 61 | //#define G_DEBUG_LOCATION_SPREAD | ... | ... |
inst/com/src/GCOMInstChars.cpp
| ... | ... | @@ -705,11 +705,145 @@ double GCOMInstChars::prob_no_selfveto(const double& energy, const double& zenit |
| 705 | 705 | ***************************************************************************/ |
| 706 | 706 | double GCOMInstChars::multi_scatter(const double& energy, const double& phigeo) const |
| 707 | 707 | { |
| 708 | - // Initialise fraction | |
| 709 | - double fraction = 1.0; | |
| 708 | + // Set integration step size | |
| 709 | + const int nz = 10; //!< Number of steps in z-direction | |
| 710 | + const int nphi = 20; //!< Number of azimuthal steps | |
| 711 | + | |
| 712 | + // Compute stuff | |
| 713 | + double alpha = energy / gammalib::mec2; | |
| 714 | + double sth = std::sin(phigeo * gammalib::deg2rad); | |
| 715 | + double cth = std::cos(phigeo * gammalib::deg2rad); | |
| 716 | + | |
| 717 | + // Get the attenuation coefficient for NE213A | |
| 718 | + double mu = 1.0 / ne213a_mfpath(energy); | |
| 719 | + | |
| 720 | + // Compute the energy after scattering and the corresponding attenuation | |
| 721 | + double epsnew = energy / (1.0 + alpha * (1.0 - cth)); | |
| 722 | + double muscat = 1.0 / ne213a_mfpath(epsnew); | |
| 723 | + | |
| 724 | + // Compute the full vertical optical depth of the module | |
| 725 | + double tau0 = mu * m_d1thick; | |
| 726 | + | |
| 727 | + // Compute the total number of interactions | |
| 728 | + double totint = 1.0 - std::exp(-tau0); | |
| 729 | + | |
| 730 | + // Perform integration per rho-value (symmetry!) over | |
| 731 | + // (1) z (the geometrical depth in the module) | |
| 732 | + // (2) phi (azimuth of scatter) | |
| 733 | + // at geometrical scatter angle phigeo. | |
| 734 | + double deltaz = m_d1thick / double(nz); | |
| 735 | + double deltau = deltaz * mu; | |
| 736 | + double dltphi = gammalib::pi / double(nphi); | |
| 737 | + | |
| 738 | + // Initialise integration loop. The binning in radius is based on | |
| 739 | + // constant surface per cylinder | |
| 740 | + double rho = 1.0; | |
| 741 | + double delrho = 2.0 * rho; | |
| 742 | + double rholow = rho - 0.5 * delrho; | |
| 743 | + double rhoupp = rho + 0.5 * delrho; | |
| 744 | + double surstp = 2.0 * delrho * rho; | |
| 745 | + | |
| 746 | + // Initialise weights | |
| 747 | + std::vector<double> w(100, 0.0); | |
| 748 | + std::vector<double> r(100*nphi, 0.0); | |
| 749 | + w[0] = surstp; | |
| 750 | + | |
| 751 | + int klast = 0; | |
| 752 | + double kappa = deltau / double(nphi); // Integration normalisation value | |
| 753 | + | |
| 754 | + // Start integration loop | |
| 755 | + double conrho = 0.0; | |
| 756 | + for (int krho = 0; krho < 100; ++krho) { | |
| 757 | + | |
| 758 | + // If we have reached the end then exit the loop now | |
| 759 | + if (klast > 0) { | |
| 760 | + break; | |
| 761 | + } | |
| 710 | 762 | |
| 711 | - // Return fraction | |
| 712 | - return fraction; | |
| 763 | + // Determine radius limits. Do not go beyond D1 module radius | |
| 764 | + if (krho > 0) { | |
| 765 | + rholow = rhoupp; | |
| 766 | + rhoupp = std::sqrt(surstp + rholow*rholow); | |
| 767 | + rho = 0.5 * (rhoupp + rholow); | |
| 768 | + w[krho] = surstp; | |
| 769 | + if (rhoupp > m_d1rad) { | |
| 770 | + rhoupp = m_d1rad; | |
| 771 | + rho = 0.5 * (rhoupp + rholow); | |
| 772 | + w[krho] = (rhoupp*rhoupp - rholow*rholow); | |
| 773 | + klast = krho; | |
| 774 | + } | |
| 775 | + } | |
| 776 | + | |
| 777 | + // Compute stuff for all azimuth angles | |
| 778 | + for (int lphi = 0; lphi < nphi; ++lphi) { | |
| 779 | + double phi = dltphi * (lphi + 0.5); | |
| 780 | + double term = rho * std::sin(phi); | |
| 781 | + r[krho+lphi*100] = -rho * std::cos(phi) + | |
| 782 | + std::sqrt(m_d1rad*m_d1rad - term*term); | |
| 783 | + } | |
| 784 | + | |
| 785 | + // Perform integration over depth | |
| 786 | + double conz = 0.0; | |
| 787 | + for (int kz = 0; kz < nz; ++kz) { | |
| 788 | + | |
| 789 | + // Calculate depth | |
| 790 | + double z = (kz + 0.5) * deltaz; | |
| 791 | + double tau = z * mu; | |
| 792 | + | |
| 793 | + // Perform integration over azimuth | |
| 794 | + double conphi = 0.0; | |
| 795 | + for (int kphi = 0; kphi < nphi; ++kphi) { | |
| 796 | + | |
| 797 | + // Test background/forward scattering | |
| 798 | + double length = 1000.0; | |
| 799 | + if (sth < 0.99) { | |
| 800 | + if (cth < 0.0) { | |
| 801 | + length = z/std::abs(cth); | |
| 802 | + } | |
| 803 | + else { | |
| 804 | + length = (m_d1thick-z)/cth; | |
| 805 | + } | |
| 806 | + } | |
| 807 | + | |
| 808 | + // | |
| 809 | + if (length * sth > r[krho+kphi*100]) { | |
| 810 | + length = r[krho+kphi*100] / sth; | |
| 811 | + } | |
| 812 | + | |
| 813 | + // Update the contribution to 1 pixel (rho,phi,z) | |
| 814 | + // from scatter phigeo | |
| 815 | + double tauran = length * muscat; | |
| 816 | + | |
| 817 | + // conphi determines contribution over phi-rings (KN | |
| 818 | + // independent of phi) => Contribution to a (rho,z) | |
| 819 | + // pixel | |
| 820 | + conphi += std::exp(-tauran); | |
| 821 | + | |
| 822 | + } // endfor: looped over azimuth | |
| 823 | + | |
| 824 | + // Average contribution must later be divided by NPHI for | |
| 825 | + // phi-pixels and multiplied by DELTAU. Do this at end, to | |
| 826 | + // save computation | |
| 827 | + conz += conphi * std::exp(-tau); | |
| 828 | + | |
| 829 | + } // endfor: looped over depth | |
| 830 | + | |
| 831 | + // Update ... | |
| 832 | + conrho += conz * kappa * w[krho]; | |
| 833 | + | |
| 834 | + } // endfor: looped over radius | |
| 835 | + | |
| 836 | + // Determine sum of weight factors for rho-average | |
| 837 | + double sumw = 0.0; | |
| 838 | + for (int krho = 0; krho < klast; ++krho) { | |
| 839 | + sumw += w[krho]; | |
| 840 | + } | |
| 841 | + | |
| 842 | + // Compute average | |
| 843 | + double trsing = conrho / (sumw * totint); | |
| 844 | + | |
| 845 | + // Return | |
| 846 | + return trsing; | |
| 713 | 847 | } |
| 714 | 848 | |
| 715 | 849 | |
| ... | ... | @@ -1367,6 +1501,77 @@ void GCOMInstChars::read_selfveto(const GFitsTable& table) |
| 1367 | 1501 | |
| 1368 | 1502 | |
| 1369 | 1503 | /***********************************************************************//** |
| 1504 | + * @brief Return NE213A mean free path | |
| 1505 | + * | |
| 1506 | + * @param[in] energy Energy (MeV) | |
| 1507 | + * @return Mean free path (cm) | |
| 1508 | + * | |
| 1509 | + * The mean free path as a function of energy, linearly interpolated between | |
| 1510 | + * data-points. Table of mean free paths for NE213A taken from L. Kuiper | |
| 1511 | + * calculated in JOB ROL-SPDSD1-43. This includes (1) Compton scattering and | |
| 1512 | + * (2) Pair production. | |
| 1513 | + ***************************************************************************/ | |
| 1514 | +double GCOMInstChars::ne213a_mfpath(const double& energy) const | |
| 1515 | +{ | |
| 1516 | + // NE213A mean free path values for energies from 1,2,...,30 MeV | |
| 1517 | + const double mfpath[] = {16.19, 23.21, 29.01, 34.04, 38.46, | |
| 1518 | + 42.35, 45.64, 48.81, 51.53, 54.17, | |
| 1519 | + 56.15, 58.09, 59.96, 61.74, 63.41, | |
| 1520 | + 64.62, 65.80, 66.94, 68.04, 69.09, | |
| 1521 | + 69.86, 70.61, 71.33, 72.03, 72.70, | |
| 1522 | + 73.33, 73.93, 74.50, 75.03, 75.51}; | |
| 1523 | + const double alpat1 = 1.95695; | |
| 1524 | + | |
| 1525 | + // Initialise mfpath | |
| 1526 | + double result = 0.0; | |
| 1527 | + | |
| 1528 | + // Do a Klein-Nishina extrapolation for small energies | |
| 1529 | + if (energy < 2.0) { | |
| 1530 | + double alpate = energy / gammalib::mec2; | |
| 1531 | + double sigat1 = kn_cross_section(1.0, alpat1) - | |
| 1532 | + kn_cross_section(-1.0, alpat1); | |
| 1533 | + double sigate = kn_cross_section(1.0, alpate) - | |
| 1534 | + kn_cross_section(-1.0, alpate); | |
| 1535 | + result = mfpath[0] * sigat1 / sigate; | |
| 1536 | + } | |
| 1537 | + | |
| 1538 | + // ... otherwise do a linear interpolation | |
| 1539 | + else { | |
| 1540 | + int index = int(energy) - 1; | |
| 1541 | + if (index >= 29) { | |
| 1542 | + index = 28; | |
| 1543 | + } | |
| 1544 | + double slope = mfpath[index+1] - mfpath[index]; | |
| 1545 | + result = mfpath[index] + slope * (energy - double(index + 1)); | |
| 1546 | + } | |
| 1547 | + | |
| 1548 | + // Return result | |
| 1549 | + return result; | |
| 1550 | +} | |
| 1551 | + | |
| 1552 | + | |
| 1553 | +/***********************************************************************//** | |
| 1554 | + * @brief Return integrated Klein-Nishina cross section | |
| 1555 | + * | |
| 1556 | + * @param[in] x cos(theta) | |
| 1557 | + * @param[in] alpha E/mc^2 | |
| 1558 | + * @return Integrated Klein-Nishina cross section | |
| 1559 | + ***************************************************************************/ | |
| 1560 | +double GCOMInstChars::kn_cross_section(const double& x, const double& alpha) const | |
| 1561 | +{ | |
| 1562 | + // Compute integrated Klein-Nishina cross section | |
| 1563 | + double y = 1.0 + alpha * (1.0 - x); | |
| 1564 | + double kn = 1.0 / (2.0 * alpha * y*y) - | |
| 1565 | + std::log(y)/alpha + | |
| 1566 | + (x*x - 1)/(alpha*y) + | |
| 1567 | + 2.0 * (x + ((1.0 + alpha)/alpha) * std::log(y)) / (alpha*alpha); | |
| 1568 | + | |
| 1569 | + // Return | |
| 1570 | + return kn; | |
| 1571 | +} | |
| 1572 | + | |
| 1573 | + | |
| 1574 | +/***********************************************************************//** | |
| 1370 | 1575 | * @brief Return minimum coefficient. |
| 1371 | 1576 | * |
| 1372 | 1577 | * @param[in] coeffs Coefficients. | ... | ... |