REST-for-Physics  v2.3
Rare Event Searches ToolKit for Physics
TRestWimpUtils.cxx
1/*************************************************************************
2 * This file is part of the REST software framework. *
3 * *
4 * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza) *
5 * For more information see http://gifna.unizar.es/trex *
6 * *
7 * REST is free software: you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation, either version 3 of the License, or *
10 * (at your option) any later version. *
11 * *
12 * REST is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have a copy of the GNU General Public License along with *
18 * REST in $REST_PATH/LICENSE. *
19 * If not, see http://www.gnu.org/licenses/. *
20 * For the list of contributors see $REST_PATH/CREDITS. *
21 *************************************************************************/
22
44
45#include <TMath.h>
46#include <TRestWimpUtils.h>
47
53const double TRestWimpUtils::GetRelativeNuclearCS(const double wimpMass, const double Anum) {
54 const double reducedMass = GetReducedMass(wimpMass, Anum);
55 const double reducedMassSingle = GetReducedMass(wimpMass, 1.);
56 return (Anum * reducedMass / reducedMassSingle) * (Anum * reducedMass / reducedMassSingle);
57}
58
63const double TRestWimpUtils::GetReducedMass(const double wimpMass, const double Anum) {
64 // WIMP mass in GeV
65 return wimpMass * GEV_PER_UMA * Anum / (wimpMass + Anum * GEV_PER_UMA);
66}
67
73const double TRestWimpUtils::GetHelmFormFactor(const double recoilEnergy, const double Anum) {
74 // Momentum transfer in keV
75 const double q = sqrt(2. * Anum * GEV_PER_UMA * 1E6 * recoilEnergy);
76 const double s = 0.9; // Femtometers-Skin thickness of the nucleus
77 const double qs = q * s / HC_KEV_FM;
78 // Parametrization of atomic nuclei
79 const double RN = sqrt(pow(1.23 * std::cbrt(Anum) - 0.60, 2) +
80 (7. / 3.) * pow(TMath::Pi(), 2) * 0.52 * 0.52 - 5. * s * s);
81 const double qR = q * RN / HC_KEV_FM;
82 // First Bessel function
83 const double bessel1 = sin(qR) / (qR * qR) - cos(qR) / qR;
84 // Form factor
85 double formFactor = 3. * bessel1 / qR * exp(-0.5 * qs * qs);
86
87 return formFactor;
88}
89
95const double TRestWimpUtils::GetVMin(const double wimpMass, const double Anum, const double recoilEnergy) {
96 const double reducedMass = GetReducedMass(wimpMass, Anum);
97 return sqrt(Anum * GEV_PER_UMA * recoilEnergy * 1E-6 / (2. * reducedMass * reducedMass)) * LIGHT_SPEED;
98}
99
105const double TRestWimpUtils::GetVelocityDistribution(const double v, const double vLab, const double vRMS,
106 const double vEscape) {
107 if (v > vLab + vEscape) return 0;
108
109 const double vAdim = vEscape / vRMS;
110 const double Nesc =
111 erf(vAdim) - 2. / sqrt(TMath::Pi()) * vAdim *
112 exp(-vAdim * vAdim); // Nesc=1 for vEscape=infinity (see Lewin&Smith appendix 1a)
113 // xMax = max(cosTheta) to meet [vec(v) + vec(vLab)]^2 < vEscape^2 boundary condition (also xMax in
114 // [-1,+1])
115 const double xMax =
116 std::max(-1., std::min(1., (vEscape * vEscape - vLab * vLab - v * v) / (2. * vLab * v)));
117
118 return v / Nesc / (vLab * vRMS * sqrt(TMath::Pi())) *
119 (exp(-(v - vLab) * (v - vLab) / (vRMS * vRMS)) -
120 exp(-(v * v + vLab * vLab + 2 * v * vLab * xMax) / (vRMS * vRMS)));
121}
122
131const double TRestWimpUtils::GetDifferentialCrossSectionNoHelmFormFactor(const double wimpMass,
132 const double crossSection,
133 const double velocity,
134 const double Anum) {
135 const double cs = GetRelativeNuclearCS(wimpMass, Anum) * crossSection;
136 const double reducedMass = GetReducedMass(wimpMass, Anum);
137 const double Emax = 1E6 / LIGHT_SPEED / LIGHT_SPEED * 2. * reducedMass * reducedMass * velocity *
138 velocity / (Anum * GEV_PER_UMA);
139
140 return cs / Emax;
141}
142
150const double TRestWimpUtils::GetDifferentialCrossSection(const double wimpMass, const double crossSection,
151 const double velocity, const double recoilEnergy,
152 const double Anum) {
153 const double formFactor = GetHelmFormFactor(recoilEnergy, Anum);
154
155 return GetDifferentialCrossSectionNoHelmFormFactor(wimpMass, crossSection, velocity, Anum) * formFactor *
156 formFactor;
157}
158
164const double TRestWimpUtils::GetRecoilRate(const double wimpMass, const double crossSection,
165 const double recoilEnergy, const double Anum, const double vLab,
166 const double vRMS, const double vEscape, const double wimpDensity,
167 const double abundance) {
168 const double vMin = GetVMin(wimpMass, Anum, recoilEnergy);
169 const double vMax = vEscape + vLab;
170 const double nNuclei = abundance * N_AVOGADRO * 1E3 / Anum; // Number of atoms
171
172 if (vMin > vMax) return 0;
173
174 double rate = 0;
175 const double velStep = 0.1; // km/s
176
177 for (double v = vMin; v < vMax; v += velStep) {
178 const double flux = 1E5 * v * wimpDensity / wimpMass;
179 const double diffRate = flux *
180 GetDifferentialCrossSection(wimpMass, crossSection, v, recoilEnergy, Anum) *
181 GetVelocityDistribution(v, vLab, vRMS, vEscape);
182 rate += diffRate * velStep;
183 }
184
185 return rate * SECONDS_PER_DAY * nNuclei;
186}
187
192const double TRestWimpUtils::GetQuenchingFactor(const double recoilEnergy, const double Anum,
193 const double Znum) {
194 const double deltaE = 0.0001, Emin = 0.1, resolution = 0.1; // keV
195 double g, Er = recoilEnergy, Ev;
196
197 do {
198 Er -= resolution;
199 g = 0.66 * pow(Znum, 5. / 18.) / sqrt(Anum) * pow(recoilEnergy, 1. / 6.);
200 } while ((Er / (1 + g) * g) > Emin);
201
202 do {
203 Er += deltaE;
204 g = 0.66 * pow(Znum, 5. / 18.) / sqrt(Anum) * pow(Er, 1. / 6.);
205 Ev = (Er / (1 + g) * g);
206 } while (recoilEnergy > Ev);
207
208 return (1 + g) / g;
209}
210
223std::map<std::string, int> TRestWimpUtils::ParseChemicalCompound(const std::string& compound) {
224 std::map<std::string, int> elementMap;
225 std::string elementName;
226 // Get the number of opnening and closing parentheses
227 std::pair<int, int> parenthesisCount = {0, 0};
228 for (size_t i = 0; i < compound.size();) {
229 if (compound[i] == '(') parenthesisCount.first++;
230 if (compound[i] == ')') parenthesisCount.second++;
231 i++;
232 }
233 if (parenthesisCount.first != parenthesisCount.second) {
234 std::cout << "Error: Parentheses in compound " << compound << " do not match." << std::endl;
235 return elementMap; // empty map
236 }
237
238 for (size_t i = 0; i < compound.size();) {
239 int coefficient = 1;
240 // Check for uppercase letter (start of an element)
241 if (std::isupper(compound[i])) {
242 elementName = compound[i];
243 i++;
244
245 // Check for lowercase letters (additional characters in element name)
246 while (i < compound.size() && std::islower(compound[i])) {
247 elementName += compound[i];
248 i++;
249 }
250
251 // Check for a number (coefficient)
252 if (i < compound.size() && std::isdigit(compound[i])) {
253 coefficient = 0;
254 while (i < compound.size() && std::isdigit(compound[i])) {
255 coefficient = coefficient * 10 + (compound[i] - '0');
256 i++;
257 }
258 }
259 // Add the element and coefficient to the map
260 elementMap[elementName] += coefficient;
261 } else if (compound[i] == '(') { // Check for a subCompound inside parentheses
262 i++;
263 std::string subCompound;
264 while (i < compound.size()) {
265 if (compound[i] == ')') {
266 if (parenthesisCount.second > 1)
267 parenthesisCount.second--;
268 else
269 break;
270 }
271 subCompound += compound[i];
272 i++;
273 }
274 i++; // Move past the closing parenthesis
275 // Find the subscript after the closing parenthesis
276 coefficient = 1;
277 if (i < compound.size() && std::isdigit(compound[i])) {
278 coefficient = 0;
279 while (i < compound.size() && std::isdigit(compound[i])) {
280 coefficient = coefficient * 10 + (compound[i] - '0');
281 i++;
282 }
283 }
284 // Recursively call the function to handle the subCompound
285 std::map<std::string, int> subElementMap = ParseChemicalCompound(subCompound);
286 for (auto& pair : subElementMap) {
287 elementMap[pair.first] += pair.second * coefficient;
288 }
289 } else
290 i++;
291 }
292 return elementMap;
293}
constexpr double GEV_PER_UMA
Physics constants.
const double GetRelativeNuclearCS(const double wimpMass, const double Anum)
Generic functions for different calculations.