REST-for-Physics  v2.3
Rare Event Searches ToolKit for Physics
src/TRestWimpSensitivity.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
121
122#include "TRestWimpSensitivity.h"
123
124#include "TRestWimpUtils.h"
125
126ClassImp(TRestWimpSensitivity);
127
128using namespace std;
129
137TRestWimpSensitivity::TRestWimpSensitivity(const char* configFilename, const string& name)
138 : TRestMetadata(configFilename) {
139 Initialize();
140
142}
147
152 SetSectionName(this->ClassName());
153 SetLibraryVersion(LIBRARY_VERSION);
154}
155
161 this->Initialize();
163 ReadNuclei();
164}
165
171 fNuclei.clear();
172 bool anyAbundanceGivenInMol = false;
173
174 // Read nuclei (standalone given) elements
175 TiXmlElement* ElementDef = GetElement("addElement");
176 while (ElementDef) {
177 TRestWimpNucleus nucleus;
178 nucleus.fNucleusName = GetFieldValue("nucleusName", ElementDef);
179 nucleus.fAnum = StringToDouble(GetFieldValue("anum", ElementDef));
180 nucleus.fZnum = StringToInteger(GetFieldValue("znum", ElementDef));
181
182 std::string el =
183 !ElementDef->Attribute("abundance") ? "Not defined" : ElementDef->Attribute("abundance");
184 if (!(el.empty() || el == "Not defined")) nucleus.fAbundance = StringToDouble(el);
185
186 el = !ElementDef->Attribute("abundanceInMol") ? "Not defined"
187 : ElementDef->Attribute("abundanceInMol");
188 if (!(el.empty() || el == "Not defined")) {
189 nucleus.fAbundanceMol = StringToDouble(el);
190 anyAbundanceGivenInMol = true;
191 }
192
193 if (nucleus.fAbundance == 0 || nucleus.fAbundanceMol == 0) {
194 if (nucleus.fAbundance == 0)
195 nucleus.fAbundance = nucleus.fAbundanceMol * nucleus.fAnum;
196 else if (nucleus.fAbundanceMol == 0)
197 nucleus.fAbundanceMol = nucleus.fAbundance / nucleus.fAnum;
198 else
199 RESTError << "abundance or abundanceInMol not defined for nucleus " << nucleus.fNucleusName
200 << RESTendl;
201 }
202 fNuclei.emplace_back(nucleus);
203 ElementDef = GetNextElement(ElementDef);
204 }
205
206 // Read nuclei (compound form given) elements
207 TiXmlElement* CompoundDef = GetElement("addCompound");
208 while (CompoundDef) {
209 bool compoundAbundanceGivenInMol = false;
210 std::string compoundName = GetFieldValue("compoundName", CompoundDef);
211 double compoundAbundance = 0, compoundAbundanceInMol = 0;
212 double totalMolMass = 0;
213
214 std::string el =
215 !CompoundDef->Attribute("abundance") ? "Not defined" : CompoundDef->Attribute("abundance");
216 if (!(el.empty() || el == "Not defined")) compoundAbundance = StringToDouble(el);
217
218 el = !CompoundDef->Attribute("abundanceInMol") ? "Not defined"
219 : CompoundDef->Attribute("abundanceInMol");
220 if (!(el.empty() || el == "Not defined")) {
221 compoundAbundanceInMol = StringToDouble(el);
222 compoundAbundanceGivenInMol = true;
223 anyAbundanceGivenInMol = true;
224 }
225
226 if (compoundAbundance == 0 && compoundAbundanceInMol == 0) {
227 RESTWarning << "abundance or abundanceInMol not defined for compound " << compoundName
228 << ". Setting its abundanceInMol to 1 " << RESTendl;
229 compoundAbundanceInMol = 1;
230 }
231
232 // Read nuclei (inside compound) elements
233 TiXmlElement* ElementDef = GetElement("addElement", CompoundDef);
234 int i = 0;
235 while (ElementDef) {
236 i++;
237 TRestWimpNucleus nucleus;
238 nucleus.fNucleusName = GetFieldValue("nucleusName", ElementDef);
239 nucleus.fAnum = StringToDouble(GetFieldValue("anum", ElementDef));
240 nucleus.fZnum = StringToInteger(GetFieldValue("znum", ElementDef));
241 totalMolMass += nucleus.fAnum * nucleus.GetStechiometricFactorFromCompound(compoundName);
242
243 fNuclei.emplace_back(nucleus);
244 ElementDef = GetNextElement(ElementDef);
245 }
246 if (compoundAbundanceGivenInMol)
247 compoundAbundance = compoundAbundanceInMol * totalMolMass;
248 else
249 compoundAbundanceInMol = compoundAbundance / totalMolMass;
250 // Set the compound abundance to all nuclei elements inside the compound
251 for (auto it = fNuclei.end() - i; it != fNuclei.end(); it++) {
252 auto& nucleus = *it;
253 int stechiometricFactor = nucleus.GetStechiometricFactorFromCompound(compoundName);
254 nucleus.fAbundanceMol = compoundAbundanceInMol * stechiometricFactor;
255 nucleus.fAbundance = nucleus.fAbundanceMol * nucleus.fAnum;
256 }
257
258 CompoundDef = GetNextElement(CompoundDef);
259 }
260
261 // Merge the repeated nuclei (same name, anum and znum) by summing their abundances
262 std::map<std::tuple<TString, Double_t, Int_t>, TRestWimpNucleus> uniqueNucleiMap;
263 for (const auto& nucleus : fNuclei) {
264 auto key = std::make_tuple(nucleus.fNucleusName, nucleus.fAnum, nucleus.fZnum);
265 if (uniqueNucleiMap.find(key) != uniqueNucleiMap.end()) {
266 uniqueNucleiMap[key].fAbundance += nucleus.fAbundance;
267 uniqueNucleiMap[key].fAbundanceMol += nucleus.fAbundanceMol;
268 } else
269 uniqueNucleiMap[key] = nucleus;
270 }
271 fNuclei.clear();
272 for (const auto& entry : uniqueNucleiMap) fNuclei.push_back(entry.second);
273
274 // normalize fAbundance (in mass only) if anyAbundanceGivenInMol
275 if (anyAbundanceGivenInMol) {
276 double sumMass = 0;
277 for (auto& nucl : fNuclei) sumMass += nucl.fAbundance;
278 for (auto& nucl : fNuclei) nucl.fAbundance /= sumMass;
279 }
280
281 for (auto& nucl : fNuclei) nucl.PrintNucleus();
282}
283
290std::map<std::string, TH1D*> TRestWimpSensitivity::GetRecoilSpectra(const double wimpMass,
291 const double crossSection) {
292 std::map<std::string, TH1D*> recoilMap;
293
294 const int nBins = (fEnergySpectra.Y() - fEnergySpectra.X()) / fEnergySpectraStep;
295
296 for (auto& nucl : fNuclei) {
297 std::string histName = "RecoilSpc_" + std::string(nucl.fNucleusName);
298 TH1D* recoilSpc =
299 new TH1D(histName.c_str(), histName.c_str(), nBins, fEnergySpectra.X(), fEnergySpectra.Y());
300
301 // Build vector of tuples=(recoilEnergy, minimum velocity, rate) used in further calculations
302 std::vector<std::tuple<double, double, double>> tEnergyVminRate;
303 for (int i = 0; i < recoilSpc->GetNbinsX(); i++) {
304 double E = recoilSpc->GetBinCenter(i);
305 if (E <= 0) continue;
306 tEnergyVminRate.push_back(
307 std::make_tuple(E, TRestWimpUtils::GetVMin(wimpMass, nucl.fAnum, E), 0));
308 }
309
310 const double nNuclei =
311 nucl.fAbundance * TRestWimpUtils::N_AVOGADRO * 1E3 / nucl.fAnum; // Number of atoms
312 const double vMin = std::get<1>(
313 tEnergyVminRate.at(0)); // element 0 should be the lowest (positive) energy -> lowest vMin
314 const double vMax = fEscapeVelocity + fLabVelocity;
315
316 // calculation of the rate for each recoil energy
317 double rate{0}; // will contain integral from minimun vMin to vMax, idem integral_min(vMin)^vMax
318 const double velStep = 0.1; // km/s
319 int j = 0;
320 double flux = 0, diffRate = 0, v = 0;
321 // vMax+velStep to save the rate when v is in interval (vMax-velStep, vMax]
322 for (v = vMin; v < vMax + velStep; v += velStep) {
323 // save (in 3rd element of tEnergyVminRate tuples) the integral from minimun vMin to each vMin,
324 // idem integral_min(vMin)^vMin
325 while (j < (int)tEnergyVminRate.size()) {
326 const double vmin = std::get<1>(tEnergyVminRate.at(j));
327 if (vmin < v) {
328 // std::get<2>(tEnergyVminRate.at(j)) = rate; //les precise
329 std::get<2>(tEnergyVminRate.at(j)) = rate - diffRate * (v - vmin); // more precise
330 j++;
331 } else
332 break;
333 }
334 flux = 1E5 * v * fWimpDensity / wimpMass;
335 diffRate =
336 flux *
337 TRestWimpUtils::GetDifferentialCrossSectionNoHelmFormFactor(wimpMass, crossSection, v,
338 nucl.fAnum) *
339 TRestWimpUtils::GetVelocityDistribution(v, fLabVelocity, fRmsVelocity, fEscapeVelocity);
340 rate += diffRate * velStep;
341 }
342 rate -=
343 diffRate * (v - vMax); // substract last diffRate*(v - vMax) to obtain the rate from vMin to vMax
344
345 /*obtain the rate (integral from each vMin to vMax) by substracting integral from minimun vMin to each
346 vMin to the integral from minimun vMin to vMax
347 idem: integral_vMin^vMax = integral_min(vMin)^vMax - integral_min(vMin)^vMin */
348 for (auto& [E, vmin, r] : tEnergyVminRate) {
349 if (vmin > vMax) continue; // r=0
350 const double formFactor = TRestWimpUtils::GetHelmFormFactor(E, nucl.fAnum);
351 r = (rate - r) * formFactor * formFactor * TRestWimpUtils::SECONDS_PER_DAY * nNuclei;
352 }
353
354 // copy results to recoilMap
355 j = 0;
356 for (int i = 0; i < recoilSpc->GetNbinsX(); i++) {
357 const double recoilEnergy = recoilSpc->GetBinCenter(i);
358 // const double recoilRate = std::get<2> (tEnergyVminRate.at(i));
359 while (j < (int)tEnergyVminRate.size()) {
360 if (recoilEnergy == std::get<0>(tEnergyVminRate.at(j))) {
361 recoilSpc->SetBinContent(i, std::get<2>(tEnergyVminRate.at(j)));
362 j++;
363 } else
364 break;
365 }
366 }
367
368 recoilMap[std::string(nucl.fNucleusName)] = recoilSpc;
369 }
370 return recoilMap;
371}
372
376const Double_t TRestWimpSensitivity::GetSensitivity(const double wimpMass) {
377 if (fNuclei.empty()) {
378 RESTError << "Please add at least one element to the rml file" << RESTendl;
379 }
380
382
383 if (!isEnergySpectraWideEnough()) {
384 RESTError << "Energy spectra range is not wide enough to match the energy range given." << RESTendl;
385 // return 0;
386 }
387
388 double nMeas = 0;
389
390 const double crossSection = 1E-45;
391 auto rSpc = GetRecoilSpectra(wimpMass, crossSection);
392
393 for (auto& nucl : fNuclei) {
394 auto recoilSpc = rSpc[std::string(nucl.fNucleusName)];
395
396 for (int i = 1; i < recoilSpc->GetNbinsX(); i++) {
397 double recoilEnergy = recoilSpc->GetBinCenter(i);
398 const double recoilRate = recoilSpc->GetBinContent(i);
399
401 recoilEnergy *= quenchingFactor[std::string(nucl.fNucleusName)]->GetBinContent(i);
402
403 if (recoilEnergy < fEnergyRange.X() || recoilEnergy > fEnergyRange.Y()) continue;
404 nMeas += recoilRate * fEnergySpectraStep;
405 }
406 }
407
408 double bckCounts = 0;
409
410 auto recSpc = rSpc[std::string(fNuclei.front().fNucleusName)];
411 for (int i = 1; i < recSpc->GetNbinsX(); i++) {
412 const double en = recSpc->GetBinCenter(i);
413 if (en < fEnergyRange.X() || en > fEnergyRange.Y()) continue;
414 bckCounts += fBackground * fEnergySpectraStep;
415 }
416 bckCounts *= fExposure;
417
418 for (auto& [name, histo] : rSpc) delete histo;
419 rSpc.clear();
420
421 RESTExtreme << "nMeas = " << nMeas << " c/kg/day" << RESTendl;
422 RESTExtreme << "bckCounts = " << bckCounts << RESTendl;
423 if (nMeas == 0) return 0;
424
425 double signalCounts = 0, prob = 0;
426
427 do {
428 prob = 0;
429 for (int mu = signalCounts; mu < (signalCounts + bckCounts + 10000); mu++) {
430 if (bckCounts <= 1.e3)
431 prob += TMath::Poisson(mu + bckCounts, bckCounts);
432 else if (bckCounts > 1.e3)
433 prob += TMath::Gaus(mu + bckCounts, bckCounts, TMath::Sqrt(bckCounts), true);
434 }
435 signalCounts++;
436 } while (fabs(prob - 0.1) > 0.01 && signalCounts < 1E6);
437
438 const double sensitivity = signalCounts * 1E-45 / (nMeas * fExposure);
439
440 RESTExtreme << "sigCounts = " << signalCounts << RESTendl;
441
442 return sensitivity;
443}
444
450 // do not calculate if already calculated (with same energy spectra limits)
451 if (!quenchingFactor.empty()) {
452 bool same = true;
453 for (auto& [name, histo] : quenchingFactor)
454 if (histo->GetXaxis()->GetXmin() != fEnergySpectra.X() ||
455 histo->GetXaxis()->GetXmax() != fEnergySpectra.Y()) {
456 same = false;
457 break;
458 }
459 if (same) return;
460 }
461
462 std::cout << "Calculating quenching factor " << std::endl;
463
464 const int nBins = (fEnergySpectra.Y() - fEnergySpectra.X()) / fEnergySpectraStep;
465
466 for (auto& nucl : fNuclei) {
467 std::string histName = "QF_" + std::string(nucl.fNucleusName);
468 TH1D* QF =
469 new TH1D(histName.c_str(), histName.c_str(), nBins, fEnergySpectra.X(), fEnergySpectra.Y());
470 for (int i = 1; i < QF->GetNbinsX(); i++) {
471 const double recoilEnergy = QF->GetBinCenter(i);
472 const double qF = TRestWimpUtils::GetQuenchingFactor(recoilEnergy, nucl.fAnum, nucl.fZnum);
473 if (!isnan(qF) && qF > 0)
474 QF->SetBinContent(i, 1. / qF);
475 else
476 QF->SetBinContent(i, 0);
477 }
478 quenchingFactor[std::string(nucl.fNucleusName)] = QF;
479 }
480}
481
482bool TRestWimpSensitivity::isEnergySpectraWideEnough() {
484 return fEnergySpectra.X() <= fEnergyRange.X() && fEnergySpectra.Y() >= fEnergyRange.Y();
485
487 for (auto& nucl : fNuclei) {
488 auto qf = quenchingFactor[std::string(nucl.fNucleusName)];
489 // assuming that Energy_nr * QF(Energy_nr) is a monotonically increasing function
490 if (qf->GetBinContent(1) * qf->GetBinCenter(1) > fEnergyRange.X() ||
491 qf->GetBinContent(qf->GetNbinsX() - 1) * qf->GetBinCenter(qf->GetNbinsX() - 1) < fEnergyRange.Y())
492 return false;
493 }
494 return true;
495}
496
501const std::string TRestWimpSensitivity::BuildOutputFileName(const std::string& extension) {
502 std::stringstream ss;
503 ss << "WimpSensitivity_";
504
505 for (auto& nucl : fNuclei) ss << nucl.fNucleusName << "_" << nucl.fAbundance << "_";
506
507 ss << "WD_" << fWimpDensity << "_";
508 ss << "Vel_" << fLabVelocity << "_" << fRmsVelocity << "_" << fEscapeVelocity << "_";
509 ss << "Bck_" << fBackground << "_";
510 ss << "Exp_" << fExposure << "_";
511 ss << "RecEn_" << fEnergySpectra.X() << "_" << fEnergySpectra.Y() << "_" << fEnergySpectraStep << "_";
512 ss << "EnRange_" << fEnergyRange.X() << "_" << fEnergyRange.Y() << "_";
513
515 ss << "usingQF";
516 else
517 ss << "noQF";
518
519 ss << extension;
520
521 std::cout << "Output File " << ss.str() << std::endl;
522
523 return ss.str();
524}
525
530std::map<std::string, TH1D*> TRestWimpSensitivity::GetFormFactor() {
531 std::map<std::string, TH1D*> formFactor;
532
533 const int nBins = (fEnergySpectra.Y() - fEnergySpectra.X()) / fEnergySpectraStep;
534
535 for (auto& nucl : fNuclei) {
536 std::string histName = "FF_" + std::string(nucl.fNucleusName);
537 TH1D* FF =
538 new TH1D(histName.c_str(), histName.c_str(), nBins, fEnergySpectra.X(), fEnergySpectra.Y());
539 for (int i = 1; i < FF->GetNbinsX(); i++) {
540 const double recoilEnergy = FF->GetBinCenter(i);
541 const double helmFF = TRestWimpUtils::GetHelmFormFactor(recoilEnergy, nucl.fAnum);
542 FF->SetBinContent(i, helmFF * helmFF);
543 }
544 formFactor[std::string(nucl.fNucleusName)] = FF;
545 }
546
547 return formFactor;
548}
549
556
557 for (auto& nucl : fNuclei) nucl.PrintNucleus();
558
559 RESTMetadata << "WimpDensity: " << fWimpDensity << " GeV/cm3" << RESTendl;
560 RESTMetadata << "WimpVelocity; VLab: " << fLabVelocity << " VRMS: " << fRmsVelocity
561 << " VEscape: " << fEscapeVelocity << " km/s" << RESTendl;
562 RESTMetadata << "Exposure: " << fExposure << " kg*day" << RESTendl;
563 RESTMetadata << "Background Level: " << fBackground << " c/keV/day" << RESTendl;
564 RESTMetadata << "Recoil energy range: (" << fEnergySpectra.X() << ", " << fEnergySpectra.Y()
565 << ") Step: " << fEnergySpectraStep << " keV" << RESTendl;
566 RESTMetadata << "Sensitivity energy range: (" << fEnergyRange.X() << ", " << fEnergyRange.Y() << ") keV"
567 << RESTendl;
568 RESTMetadata << "Use quenching factor: " << (fUseQuenchingFactor ? "true" : "false") << RESTendl;
569 RESTMetadata << "+++++" << RESTendl;
570}
A base class for any REST metadata class.
Definition: TRestMetadata.h:74
virtual void PrintMetadata()
Implemented it in the derived metadata class to print out specific metadata information.
endl_t RESTendl
Termination flag object for TRestStringOutput.
TiXmlElement * GetElement(std::string eleDeclare, TiXmlElement *e=nullptr)
Get an xml element from a given parent element, according to its declaration.
Int_t LoadConfigFromFile(const std::string &configFilename, const std::string &sectionName="")
Give the file name, find out the corresponding section. Then call the main starter.
virtual void InitFromConfigFile()
To make settings from rml file. This method must be implemented in the derived class.
void SetLibraryVersion(TString version)
Set the library version of this metadata class.
std::string GetFieldValue(std::string parName, TiXmlElement *e)
Returns the field value of an xml element which has the specified name.
void SetSectionName(std::string sName)
set the section name, clear the section content
std::string fConfigFileName
Full name of the rml file.
TiXmlElement * GetNextElement(TiXmlElement *e)
Get the next sibling xml element of this element, with same eleDeclare.
A class to store different nucleus parameters.
Double_t fAnum
Atomic number in amus.
int GetStechiometricFactorFromCompound(const std::string &compound)
Get the stechiometric factor of this nucleus in a given compound.
TString fNucleusName
Nucleus name.
Double_t fAbundance
Abundance, in mass percentage.
Int_t fZnum
Number of protons.
Double_t fAbundanceMol
Abundance, in mole (or volume)
void Initialize() override
Initialization of TRestWimpSensitivity members.
Double_t fExposure
Detector exposure in kg*day.
~TRestWimpSensitivity()
Default destructor.
void InitFromConfigFile() override
Initialization of TRestWimpSensitivity members through a RML file.
std::map< std::string, TH1D * > GetRecoilSpectra(const double wimpMass, const double crossSection)
Get recoil spectra for a given WIMP mass and cross section Better performance version (velocity integ...
Double_t fEscapeVelocity
WIMP escape velocity (km/s)
TVector2 fEnergySpectra
Energy range for the recoil spectra in keV.
Double_t fEnergySpectraStep
Energy step for the recoil spectra in keV.
void PrintMetadata() override
Prints on screen the details about WIMP parameters, stored in TRestWimpSensitivity.
const Double_t GetSensitivity(const double wimpMass)
Get sensitivity for a give WIMP mass.
TVector2 fEnergyRange
Energy range for the sensitivity prospects in keV.
Double_t fBackground
Detector background level in c/keV/day.
std::map< std::string, TH1D * > quenchingFactor
Map containing quenching factor for a nucleus.
void CalculateQuenchingFactor()
Calculate Quenching factor and stores in a map.
Bool_t fUseQuenchingFactor
Use or not quenching factor.
Double_t fRmsVelocity
WIMP RMS velocity (km/s)
Double_t fWimpDensity
WIMP density in GeV/cm3.
Double_t fLabVelocity
WIMP velocity in the lab (Earth) frame reference in km/s.
void ReadNuclei()
Initialization of TRestWimpSensitivity members through a RML file.
TRestWimpSensitivity(const char *configFilename, const std::string &name="")
Constructor loading data from a config file.
std::map< std::string, TH1D * > GetFormFactor()
Return a map of histograms with the Form Factor of the different elements.
const std::string BuildOutputFileName(const std::string &extension=".txt")
Return output file format with different parameters used in the calculation.
std::vector< TRestWimpNucleus > fNuclei
A vector containing TRestWimpNucleus with different nucleus parameters.
Double_t StringToDouble(std::string in)
Gets a double from a string.
Int_t StringToInteger(std::string in)
Gets an integer from a string.