MARLEY (Model of Argon Reaction Low Energy Yields) v2.0.0
A Monte Carlo event generator for tens-of-MeV neutrino interactions
Loading...
Searching...
No Matches
marley_utils.hh
1
4//
5// This file is part of MARLEY (Model of Argon Reaction Low Energy Yields)
6//
7// MARLEY is free software: you can redistribute it and/or modify it under the
8// terms of version 3 of the GNU General Public License as published by the
9// Free Software Foundation.
10//
11// For the full text of the license please see COPYING or
12// visit http://opensource.org/licenses/GPL-3.0
13//
14// Please respect the MCnet academic usage guidelines. See GUIDELINES
15// or visit https://www.montecarlonet.org/GUIDELINES for details.
16
17// Nonstandard but widely-supported (see
18// http://en.wikipedia.org/wiki/Pragma_once) preprocessor directive that
19// prevents this file from being included multiple times. Another option is an
20// include guard (http://en.wikipedia.org/wiki/Include_guard).
21#pragma once
22
23#include <algorithm>
24#include <chrono>
25#include <cmath>
26#include <complex>
27#include <functional>
28#include <limits>
29#include <random>
30#include <regex>
31#include <sstream>
32#include <string>
33#include <unordered_map>
34
35namespace marley_utils {
36
37 // Frequently used particle IDs
38 constexpr int PHOTON = 22;
39 constexpr int ELECTRON = 11;
40 constexpr int POSITRON = -11;
41 constexpr int ELECTRON_NEUTRINO = 12;
42 constexpr int ELECTRON_ANTINEUTRINO = -12;
43 constexpr int MUON = 13;
44 constexpr int MUON_NEUTRINO = 14;
45 constexpr int MUON_ANTINEUTRINO = -14;
46 constexpr int TAU = 15;
47 constexpr int TAU_NEUTRINO = 16;
48 constexpr int TAU_ANTINEUTRINO = -16;
49 constexpr int NEUTRON = 2112;
50 constexpr int PROTON = 2212;
51 constexpr int DEUTERON = 1000010020;
52 constexpr int TRITON = 1000010030;
53 constexpr int HELION = 1000020030;
54 constexpr int ALPHA = 1000020040;
55
56 // Dummy double value representing an unknown maximum PDF value. Signals to
57 // marley::Generator::rejection_sample that it needs to search for the
58 // maximum before doing the sampling.
59 constexpr double UNKNOWN_MAX = std::numeric_limits<double>::infinity();
60
61 // Limits rejection sampling loops to a finite but large number of iterations
62 // before taking corrective action to avoid "getting stuck"
63 constexpr int LARGE_NUMBER_OF_ITERATIONS = 100000;
64
65 // Fermi coupling constant (MeV^(-2))
66 constexpr double GF = 1.16637e-11;
67 // Square of the Fermi coupling constant (MeV^(-4))
68 constexpr double GF2 = GF * GF;
69
70 // Absolute value of the CKM matrix element for mixing between the up and
71 // down quarks abs(V_ud)
72 constexpr double Vud = 0.97427;
73 // Square of abs(V_ud)
74 constexpr double Vud2 = Vud * Vud;
75
76 // sin^2(theta_W) (sine squared of the weak mixing angle)
77 // Effective value taken from 2014 PDG Review of Particle Physics,
78 // Table 1.1, "Physical Constants"
79 constexpr double sin2thetaw = 0.23155;
80
81 // Conversion factor to use when expressing ENSDF energies (keV) in
82 // standard MARLEY energy units (MeV)
83 constexpr double MeV = 1e-3;
84
85 // Conversion factor to use when expressing atomic masses (micro-amu)
86 // in standard MARLEY energy units (MeV)
87 constexpr double micro_amu = 0.000931494061;
88
89 // Infinities
90 constexpr double infinity = std::numeric_limits<double>::max();
91 constexpr double minus_infinity = -infinity;
92
93 // Muon mass
94 constexpr double m_mu = 113428.9267; // micro-amu
95
96 // Consistent value of pi to use throughout all of MARLEY
97 constexpr double pi = M_PI;
98 constexpr double two_pi = 2.*pi;
99 const double sqrt_two_pi = std::sqrt( two_pi );
100 constexpr double half_pi = pi/2.0;
101
102 // Imaginary unit
103 constexpr std::complex<double> i(0, 1);
104
105 // Natural logarithm of 2
106 const double log_2 = std::log(2);
107
108 // The physical constants given here were taken from
109 // the 2014 edition of the Review of Particle Physics
110 // published by the Particle Data Group, except where
111 // otherwise noted.
112
113 // Fine structure constant
114 constexpr double alpha = 7.2973525698e-3;
115
116 // Conversion factor used to switch to natural units (hbar = c = 1)
117 constexpr double hbar_c = 197.3269718; // MeV*fm
118 constexpr double hbar_c2 = hbar_c * hbar_c; // MeV^2 * fm^2
119
120 // Need to convert tabulated half-lives (s) to natural units (1/MeV)
121 constexpr double hbar = 6.58211951e-22; // MeV * s
122
123 // Electron mass
124 constexpr double m_e = 0.510998928; // MeV
125 // Proton mass (from 2023 PDG Review of Particle Physics)
126 constexpr double m_p = 938.27208816; // MeV
127 // Neutron mass (from 2023 PDG Review of Particle Physics)
128 constexpr double m_n = 939.5654205; // MeV
129 // Nucleon mass (taken as the average of the proton and neutron masses)
130 constexpr double m_nucleon = 0.5 * (m_p + m_n); // MeV
131 // Square of the nucleon mass
132 constexpr double m_nucleon2 = m_nucleon * m_nucleon; // MeV^2
133 // Charged pion mass (from 2023 PDG Review of Particle Physics)
134 constexpr double m_pion = 139.57039; // MeV
135 // Proton magnetic moment (from 2023 PDG Review of Particle Physics)
136 constexpr double mu_p = 2.79284734463; // mu_N (nuclear magneton)
137 // Neutron magnetic moment (from 2023 PDG Review of Particle Physics)
138 constexpr double mu_n = -1.9130427; // mu_N (nuclear magneton)
139
140 // Nucleon vector coupling constant
141 constexpr double g_V = 1.0;
142 constexpr double g_V2 = g_V * g_V;
143
144 // Nucleon axial-vector coupling constant
145 constexpr double g_A = 1.262;
146 constexpr double g_A2 = g_A * g_A;
147
148 // Mass parameter for nucleon dipole Sachs form factors
149 constexpr double M_V = 0.84 * 1e3; // MeV
150
151 // Mass parameter for nucleon dipole axial form factors
152 constexpr double M_A = 1.032 * 1e3; // MeV
153
154 // Constant to use when converting from mb to MeV^(-2)
155 constexpr double mb = 1/3.89379338e5; // MeV^(-2) mb^(-1)
156 // Constant to use to convert from fm^2 to 10^(-40) cm^2
157 constexpr double fm2_to_minus40_cm2 = 1e14;
158 // Constant to use to convert from fm^2 to picobarn
159 constexpr double fm2_to_picobarn = 1e10;
160 // Constant to use when converting fm to cm
161 constexpr double fm_to_cm = 1e-13;
162 // Square of the elementary charge
163 constexpr double e2 = hbar_c * alpha; // MeV*fm
164 // Constant to use when approximating nuclear radii via
165 // r = r0 * A^(1/3), where A is the nucleus's mass number.
166 // See, for example, Introductory Nuclear Physics by Kenneth S. Krane.
167 constexpr double r0 = 1.2; // fm
168 // Handy constants for the fractions 1/2 and 1/3
169 constexpr double ONE_HALF = 1.0/2.0;
170 constexpr double ONE_THIRD = 1.0/3.0;
171
172 // Strings to use for latex table output of DecayScheme objects
173 extern std::string latex_table_1, latex_table_2, latex_table_3, latex_table_4;
174
175 // Create an ENSDF nucid string given a nuclide's atomic number Z
176 // and mass number A
177 std::string nuc_id(int Z, int A);
178
179 // Return the PDG particle ID that corresponds to a ground-state
180 // nucleus with atomic number Z and mass number A
181 inline int get_nucleus_pid(int Z, int A) {
182 if (Z == 0 && A == 1) return NEUTRON;
183 else if (Z == 1 && A == 1) return PROTON;
184 else return 10000*Z + 10*A + 1000000000;
185 }
186
187 inline int get_particle_Z(int pid) {
188 if (pid == marley_utils::PROTON) return 1;
189 else if (pid == marley_utils::NEUTRON) return 0;
190 // nuclear fragment
191 else if (pid > 1000000000) return (pid % 10000000)/10000;
192 // other particle
193 else return 0;
194 }
195
196 inline int get_particle_A(int pid) {
197 if (pid == marley_utils::PROTON) return 1;
198 else if (pid == marley_utils::NEUTRON) return 1;
199 // nuclear fragment
200 else if (pid > 1000000000) return (pid % 10000)/10;
201 // other particle
202 else return 0;
203 }
204
209 bool string_to_neutrino_pdg(const std::string& str, int& pdg);
210
214 std::string neutrino_pdg_to_string(int pdg);
215
219 inline bool is_lepton( int pdg ) {
220 int abs_pdg = std::abs( pdg );
221 bool is_a_lepton = ( abs_pdg >= ELECTRON && abs_pdg <= TAU_NEUTRINO );
222 return is_a_lepton;
223 }
224
225 // Assign a helicity value based on the PDG code and check its validity
226 int get_particle_helicity( const int pdg );
227
233 inline bool is_ion( int pdg ) {
234 bool is_an_ion = ( pdg > 1000000000 && pdg < 2000000000 );
235 return is_an_ion;
236 }
237
238 // Take the square root of a number. Assume that a negative argument is
239 // due to roundoff error and return zero in such cases rather than NaN.
240 double real_sqrt(double num);
241
242 // A function template that will raise a number to an integer power.
243 // We can usually use std::pow for this sort of thing (and, unlike this
244 // approach, fractional powers are also supported by that one). However, since
245 // std::pow is not constexpr, this is a workaround.
246 // This function was taken from from https://tinyurl.com/constexpr-pow.
247 template <typename T> constexpr T ipow(T num, unsigned int pow)
248 {
249 return ( pow >= sizeof(unsigned int)*8 ) ? 0 :
250 pow == 0 ? 1 : num * ipow(num, pow - 1);
251 }
252
253 // Compute the complex gamma function using the Lanczos approximation
254 std::complex<double> gamma(std::complex<double> z);
255
256 // Numerically integrate a 1D function using Clenshaw-Curtis quadrature
257 double num_integrate(const std::function<double(double)> &f,
258 double a, double b);
259
260 // Numerically minimize or maximize a function of one variable using
261 // Brent's method (see http://en.wikipedia.org/wiki/Brent%27s_method)
262 double minimize(const std::function<double(double)> f, double leftEnd,
263 double rightEnd, double epsilon, double& minLoc);
264
265 double maximize(const std::function<double(double)> f, double leftEnd,
266 double rightEnd, double epsilon, double& maxLoc);
267
268 // Find both solutions of a quadratic equation while attempting
269 // to avoid floating-point arithmetic issues
270 void solve_quadratic_equation(double A, double B,
271 double C, double &solPlus, double &solMinus);
272
273 // Efficiently read in an entire file as a std::string
274 std::string get_file_contents(std::string filename);
275
276 // Advance to the next line of an ifstream that either matches (match == true)
277 // or does not match (match == false) a given regular expression
278 std::string get_next_line(std::ifstream &file_in, const std::regex &rx,
279 bool match);
280 // Do the same, but store the number of lines used in num_lines
281 std::string get_next_line(std::ifstream &file_in, const std::regex &rx,
282 bool match, int& num_lines);
283
284 // String containing all of the characters that will be
285 // considered whitespace by default in the string
286 // manipulation functions below
287 const std::string whitespace = " \f\n\r\t\v";
288
289 // This version of std::stod will return 0 if it encounters
290 // an empty string or an all-whitespace string.
291 inline double str_to_double(const std::string& s) {
292 size_t endpos = s.find_last_not_of(whitespace);
293 if (endpos == std::string::npos) {
294 return 0.0; // string was all whitespace
295 }
296 else {
297 return std::stod(s);
298 }
299 }
300
301 // Function that creates a copy of a std::string object
302 // that has been converted to all lowercase
303 inline std::string to_lowercase(const std::string& s) {
304 std::string new_s = s;
305 std::transform(new_s.begin(), new_s.end(), new_s.begin(), ::tolower);
306 return new_s;
307 }
308
309 // Function that converts a std::string object to
310 // all lowercase in place and returns a reference to
311 // it afterwards
312 inline std::string& to_lowercase_inplace(std::string& s) {
313 std::transform(s.begin(), s.end(), s.begin(), ::tolower);
314 return s;
315 }
316
317 // Function that converts a std::string object to
318 // all uppercase in place and returns a reference to
319 // it afterwards
320 inline std::string& to_uppercase_inplace(std::string& s) {
321 std::transform(s.begin(), s.end(), s.begin(), ::toupper);
322 return s;
323 }
324
325 // Functions for padding std::string objects in place. They all return
326 // references to the string afterwards. These functions are based on
327 // http://stackoverflow.com/a/667219/4081973
328 inline std::string& pad_left_inplace(std::string &str,
329 const size_t len, const char pad_char = ' ')
330 {
331 if(len > str.size())
332 str.insert(0, len - str.size(), pad_char);
333 return str;
334 }
335
336 inline std::string& pad_right_inplace(std::string &str,
337 const size_t len, const char pad_char = ' ')
338 {
339 if(len > str.size())
340 str.append(len - str.size(), pad_char);
341 return str;
342 }
343
344 // These std::string trimming functions were taken from code
345 // presented here: http://www.cplusplus.com/faq/sequences/strings/trim/
346 // and here: http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring
347 // The first three (with the suffix copy) return a trimmed copy of
348 // the string without modifying the original.
349 inline std::string trim_right_copy(const std::string& s,
350 const std::string& delimiters = whitespace)
351 {
352 size_t endpos = s.find_last_not_of(delimiters);
353 return (endpos == std::string::npos) ? "" : s.substr(0, endpos + 1);
354 }
355
356 inline std::string trim_left_copy(const std::string& s,
357 const std::string& delimiters = whitespace)
358 {
359 size_t startpos = s.find_first_not_of(delimiters);
360 return (startpos == std::string::npos) ? "" : s.substr(startpos);
361 }
362
363 inline std::string trim_copy(const std::string& s,
364 const std::string& delimiters = whitespace)
365 {
366 return trim_left_copy(trim_right_copy(s, delimiters), delimiters);
367 }
368
369 // The second three alter the original string, returning a
370 // reference to it after it has been trimmed.
371 inline std::string& trim_right_inplace(std::string& s,
372 const std::string& delimiters = whitespace)
373 {
374 size_t endpos = s.find_last_not_of(delimiters);
375 if (endpos == std::string::npos) {
376 s.clear();
377 }
378 else {
379 s.erase(endpos + 1);
380 }
381 return s;
382 }
383
384 inline std::string& trim_left_inplace(std::string& s,
385 const std::string& delimiters = whitespace)
386 {
387 size_t startpos = s.find_first_not_of(delimiters);
388 if (startpos == std::string::npos) {
389 s.clear();
390 }
391 else {
392 s.erase(0, startpos);
393 }
394 return s;
395 }
396
397 inline std::string& trim_inplace(std::string& s,
398 const std::string& delimiters = whitespace)
399 {
400 return trim_left_inplace(trim_right_inplace(s,delimiters), delimiters);
401 }
402
403 // Split a string into substrings separated by a single-character
404 // delimiter. Return a vector loaded with the resulting array of strings.
405 // Based on http://www.martinbroadhurst.com/how-to-split-a-string-in-c.html
406 inline std::vector<std::string> split_string(const std::string& str,
407 char delim = ' ')
408 {
409 std::vector<std::string> vec;
410 std::stringstream ss( str );
411 std::string token;
412 while ( std::getline(ss, token, delim) ) {
413 vec.push_back( token );
414 }
415 return vec;
416 }
417
418 // Function that takes a number of bytes and returns a string
419 // representing the amount of memory in more readable units
420 std::string num_bytes_to_string(double bytes, unsigned precision = 3);
421
422 // Trim an ENSDF nucid string and make two-letter element symbols have a
423 // lowercase last letter. Currently, no checking is done to see if the
424 // string is a valid nucid.
425 std::string nucid_to_symbol(std::string nucid);
426
427 // Similar to nucid_to_symbol, but returns the atomic number as an integer
428 // instead
429 int nucid_to_Z(std::string nucid);
430
431 // Generalized std::chrono::duration helper types
432 template <typename repType> using
433 seconds = std::chrono::duration< repType >;
434 template <typename repType> using
435 minutes = std::chrono::duration< repType, std::ratio<60> >;
436 template <typename repType> using
437 hours = std::chrono::duration< repType, std::ratio<3600> >;
438 template <typename repType> using
439 days = std::chrono::duration< repType, std::ratio<86400> >;
440
441 // This function is a generalized version of code taken from the accepted answer at
442 // http://stackoverflow.com/questions/15957805/extract-year-month-day-etc-from-stdchronotime-point-in-c
443 //
444 // Function that returns a string representation (in the
445 // format days hours:minutes:seconds) of a std::chrono::duration object
446 template <typename repType, typename periodType = std::ratio<1>> std::string duration_to_string(
447 std::chrono::duration<repType, periodType> duration)
448 {
449 int day_count = static_cast<int>(std::chrono::duration_cast
450 <marley_utils::days<repType>>(duration) / (marley_utils::days<repType>(1)));
451 duration -= marley_utils::days<repType>(day_count);
452
453 int hour_count = static_cast<int>(std::chrono::duration_cast
454 <marley_utils::hours<repType>>(duration) / (marley_utils::hours<repType>(1)));
455 duration -= marley_utils::hours<repType>(hour_count);
456
457 int minute_count = static_cast<int>(std::chrono::duration_cast
458 <marley_utils::minutes<repType>>(duration) / (marley_utils::minutes<repType>(1)));
459 duration -= marley_utils::minutes<repType>(minute_count);
460
461 int second_count = static_cast<int>(std::chrono::duration_cast
462 <marley_utils::seconds<repType>>(duration) / (marley_utils::seconds<repType>(1)));
463 duration -= marley_utils::seconds<repType>(second_count);
464
465 std::ostringstream out;
466 if (day_count > 1) {
467 out << day_count << " days ";
468 }
469 if (day_count == 1) {
470 out << day_count << " day ";
471 }
472 if (hour_count < 10) out << "0";
473 out << hour_count << ":";
474 if (minute_count < 10) out << "0";
475 out << minute_count << ":";
476 if (second_count < 10) out << "0";
477 out << second_count;
478
479 return out.str();
480 }
481
482 template <typename durationType> std::string duration_to_string(
483 durationType duration)
484 {
485 return duration_to_string<typename durationType::rep,
486 typename durationType::period>(duration);
487 }
488
489 // Function that takes two std::system_clock::time_point objects and returns
490 // a string (in the same format as marley_utils::duration_to_string)
491 // representing the time between them
492 std::string elapsed_time_string(
493 std::chrono::system_clock::time_point &start_time,
494 std::chrono::system_clock::time_point &end_time);
495
496 // Lookup table for particle symbols (keys are PDG particle IDs,
497 // values are symbols).
498 const std::unordered_map<int, std::string> particle_symbols = {
499 { 0, "∅" }, // dummy/absent particle (non-standard PDG code 0)
500 { 12, "νe" },
501 { 14, "νμ" },
502 { 16, "ντ" },
503 { 11, "e" },
504 { 13, "μ" },
505 { 15, "τ" },
506 { 22, "γ" },
507 { 2112, "n" },
508 { 2212, "p" },
509 { 1000010020, "d" },
510 { 1000010030, "t" },
511 { 1000020030, "h" },
512 { 1000020040, "α" },
513 };
514
519 std::string get_particle_symbol( int pid, bool excited = false );
520
521 // Lookup table for particle electric charges (keys are PDG particle IDs,
522 // values are charges expressed as integer multiples of the proton charge).
523 const std::unordered_map<int, int> particle_electric_charges = {
524 { 0, 0 }, // dummy/absent particle (non-standard PDG code 0)
525 { 11, -1 },
526 { 12, 0 },
527 { 13, -1 },
528 { 14, 0 },
529 { 15, -1 },
530 { 16, 0 },
531 { 22, 0 },
532 { 2112, 0 },
533 { 2212, 1 }
534 };
535
536 // Looks up the electric charge of a particle based on its PDG particle ID
537 inline int get_particle_charge(int pid) {
538 // If a nuclear particle ID is supplied to this function, assume
539 // that it is a bare nucleus, and return its atomic number Z.
540 if (pid > 1000000000) return (pid % 10000000)/10000;
541 // Otherwise, use the lookup table to determine the charge
542 int charge = particle_electric_charges.at( std::abs(pid) );
543 // The lookup table only contains particles (as opposed to antiparticles).
544 // If an antiparticle was requested, return the opposite electric charge.
545 if ( pid < 0 ) charge *= -1;
546 return charge;
547 }
548
549 // Prompt the user with a yes/no question and retrieve the result
550 bool prompt_yes_no(const std::string& message);
551
552 // Lookup table for element symbols (keys are atomic numbers Z,
553 // values are symbols on the periodic table). The symbol "Nn" is
554 // used for a neutron to match the ENSDF convention.
555 extern const std::unordered_map<int, std::string> element_symbols;
556
557 // Lookup table for atomic numbers (keys are symbols on the periodic table,
558 // values are atomic numbers Z). The symbol "Nn" is used for a neutron to
559 // match the ENSDF convention.
560 extern const std::unordered_map<std::string, int> atomic_numbers;
561
562 extern const std::string marley_logo;
563
564 extern const std::string marley_pic;
565}