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.cc
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#include <algorithm>
18#include <cctype>
19#include <cfloat>
20#include <cmath>
21#include <complex>
22#include <fstream>
23#include <iostream>
24#include <map>
25#include <random>
26#include <regex>
27#include <string>
28#include <vector>
29
30#include "marley/marley_utils.hh"
31#include "marley/Integrator.hh"
32#include "marley/Error.hh"
33
34// Strings to use for latex table output of ENSDF data
35std::string marley_utils::latex_table_1 = "\\documentclass[12pt]{article}\n"
36 "\n"
37 "\\usepackage{amsmath}\n"
38 "\\usepackage{booktabs}\n"
39 "\\usepackage[justification=justified,\n"
40 "width=\\textwidth,\n"
41 "labelformat=empty]{caption}\n"
42 "\\usepackage[top=1in, bottom=1in, left=0.25in, right=0.25in]{geometry}\n"
43 "\\usepackage{isotope}\n"
44 "\\usepackage{longtable}\n"
45 "\\usepackage{multirow}\n"
46 "\\usepackage{siunitx}\n"
47 "\n"
48 "\\newcommand{\\ExtraRowSpace}{1cm}\n"
49 "\n"
50 "\\begin{document}\n"
51 "\\begin{center}\n"
52 "\\begin{longtable}{\n"
53 "S[table-number-alignment = center,\n"
54 " table-text-alignment = center]\n"
55 "c\n"
56 "S[table-number-alignment = center,\n"
57 " table-text-alignment = center]\n"
58 "% table-column-width = 2cm]\n"
59 "S[table-number-alignment = center,\n"
60 " table-text-alignment = center]\n"
61 "S[table-number-alignment = center,\n"
62 " table-text-alignment = center]\n"
63 "}\n"
64 "\\caption";
65
66std::string marley_utils::latex_table_2 = "\\toprule\n"
67 "%{\\centering\\textbf{Level Energy (keV)}}\n"
68 "%& {\\centering\\textbf{Spin-Parity}}\n"
69 "%& {\\centering\\textbf{$\\boldsymbol{\\gamma}$ Energy (keV)}}\n"
70 "%& {\\centering\\textbf{$\\boldsymbol{\\gamma}$ RI}}\n"
71 "%& {\\centering\\textbf{Final Energy (keV)}} \\\\\n"
72 "{\\multirow{3}{2cm}{\\centering\\textbf{Level Energy (keV)}}}\n"
73 "& {\\multirow{3}{2cm}{\\centering\\textbf{Spin-Parity}}}\n"
74 "& {\\multirow{3}{2cm}{\\centering\\textbf{$\\boldsymbol{\\gamma}$ Energy (keV)}}}\n"
75 "& {\\multirow{3}{2cm}{\\centering\\textbf{$\\boldsymbol{\\gamma}$ RI}}}\n"
76 "& {\\multirow{3}{2.5cm}{\\centering\\textbf{Final Level Energy (keV)}}} \\\\\n"
77 "& & & & \\\\\n"
78 "& & & & \\\\\n"
79 "\\midrule\n"
80 "\\endfirsthead\n"
81 "\\caption[]";
82
83std::string marley_utils::latex_table_3 = "\\toprule\n"
84 "%{\\centering\\textbf{Level Energy (keV)}}\n"
85 "%& {\\centering\\textbf{Spin-Parity}}\n"
86 "%& {\\centering\\textbf{$\\boldsymbol{\\gamma}$ Energy (keV)}}\n"
87 "%& {\\centering\\textbf{$\\boldsymbol{\\gamma}$ RI}}\n"
88 "%& {\\centering\\textbf{Final Energy (keV)}} \\\\\n"
89 "{\\multirow{3}{2cm}{\\centering\\textbf{Level Energy (keV)}}}\n"
90 "& {\\multirow{3}{2cm}{\\centering\\textbf{Spin-Parity}}}\n"
91 "& {\\multirow{3}{2cm}{\\centering\\textbf{$\\boldsymbol{\\gamma}$ Energy (keV)}}}\n"
92 "& {\\multirow{3}{2cm}{\\centering\\textbf{$\\boldsymbol{\\gamma}$ RI}}}\n"
93 "& {\\multirow{3}{2.5cm}{\\centering\\textbf{Final Level Energy (keV)}}} \\\\\n"
94 "& & & & \\\\\n"
95 "& & & & \\\\\n"
96 "\\midrule\n"
97 "\\noalign{\\vspace{-\\ExtraRowSpace}}\n"
98 "\\endhead\n"
99 "\\bottomrule \\multicolumn{4}{r}{\\textit{Continued on next page}} \\\\\n"
100 "\\endfoot\n"
101 "\\bottomrule\n"
102 "\\endlastfoot\n"
103 "% Table data\n"
104 "\n";
105
106std::string marley_utils::latex_table_4 = "\\end{longtable}\n"
107 "\\end{center}\n"
108 "\\end{document}";
109
110// This implementation of the complex gamma function is based on the
111// Lanczos approximation and its Python implementation given
112// on Wikipedia (https://en.wikipedia.org/wiki/Lanczos_approximation)
113// The C++ version given here is taken almost verbatim from
114// http://bytes.com/topic/c/answers/576697-c-routine-complex-gamma-function
115std::complex<double> marley_utils::gamma(std::complex<double> z)
116{
117 // Initialize some constants used in the algorithm. The "static
118 // const" keywords ensure that these constants are initialized only
119 // once (not reinitialized each time this function is called)
120 static const int g=7;
121 static const double pi =
122 3.1415926535897932384626433832795028841972;
123 static const double p[g+2] = {0.99999999999980993, 676.5203681218851,
124 -1259.1392167224028, 771.32342877765313, -176.61502916214059,
125 12.507343278686905, -0.13857109526572012, 9.9843695780195716e-6,
126 1.5056327351493116e-7};
127
128 if (std::real(z) < 0.5) {
129 return pi / (std::sin(pi*z)*gamma(1.0-z));
130 }
131
132 z -= 1.0;
133
134 std::complex<double> x = p[0];
135
136 for (int j = 1; j < g + 2; ++j) {
137 x += p[j]/(z+std::complex<double>(j,0));
138 }
139
140 std::complex<double> t = z + (g + 0.5);
141
142 return std::sqrt(2*pi) * std::pow(t, z + 0.5) * std::exp(-t) * x;
143}
144
145// This function is a modified version of a public-domain implementation of
146// Brent's algorithm for minimizing a function. You can download the original
147// source code from http://tinyurl.com/hhoy3ky.
148
149// The return value of minimize is the minimum of the function f.
150// The location where f takes its minimum is returned in the variable minLoc.
151// Notation and implementation based on Chapter 5 of Richard Brent's book
152// "Algorithms for Minimization Without Derivatives".
153double marley_utils::minimize(const std::function<double(double)> f, // [in] objective function to minimize
154 double leftEnd, // [in] smaller value of bracketing interval
155 double rightEnd, // [in] larger value of bracketing interval
156 double epsilon, // [in] stopping tolerance
157 double& minLoc) // [out] location of minimum
158{
159 double d, e, m, p, q, r, tol, t2, u, v, w, fu, fv, fw, fx;
160 static const double c = 0.5*(3.0 - std::sqrt(5.0));
161 static const double SQRT_DBL_EPSILON = std::sqrt(DBL_EPSILON);
162
163 double& a = leftEnd;
164 double& b = rightEnd;
165 double& x = minLoc;
166
167 v = w = x = a + c*(b - a);
168 d = e = 0.0;
169 fv = fw = fx = f(x);
170
171 // Check stopping criteria
172 while (m = 0.5*(a + b),
173 tol = SQRT_DBL_EPSILON*fabs(x) + epsilon,
174 t2 = 2.0*tol,
175 fabs(x - m) > t2 - 0.5*(b - a))
176 {
177 p = q = r = 0.0;
178 if (fabs(e) > tol)
179 {
180 // fit parabola
181 r = (x - w)*(fx - fv);
182 q = (x - v)*(fx - fw);
183 p = (x - v)*q - (x - w)*r;
184 q = 2.0*(q - r);
185 (q > 0.0) ? p = -p : q = -q;
186 r = e; e = d;
187 }
188 if (fabs(p) < fabs(0.5*q*r) && p < q*(a - x) && p < q*(b - x))
189 {
190 // A parabolic interpolation step
191 d = p/q;
192 u = x + d;
193 // f must not be evaluated too close to a or b
194 if (u - a < t2 || b - u < t2)
195 d = (x < m) ? tol : -tol;
196 }
197 else
198 {
199 // A golden section step
200 e = (x < m) ? b : a;
201 e -= x;
202 d = c*e;
203 }
204 // f must not be evaluated too close to x
205 if (fabs(d) >= tol)
206 u = x + d;
207 else if (d > 0.0)
208 u = x + tol;
209 else
210 u = x - tol;
211 fu = f(u);
212 // Update a, b, v, w, and x
213 if (fu <= fx)
214 {
215 (u < x) ? b = x : a = x;
216 v = w; fv = fw;
217 w = x; fw = fx;
218 x = u; fx = fu;
219 }
220 else
221 {
222 (u < x) ? a = u : b = u;
223 if (fu <= fw || w == x)
224 {
225 v = w; fv = fw;
226 w = u; fw = fu;
227 }
228 else if (fu <= fv || v == x || v == w)
229 {
230 v = u; fv = fu;
231 }
232 }
233 }
234 return fx;
235}
236
237// We can maximize a function using the same technique by minimizing its
238// opposite
239double marley_utils::maximize(const std::function<double(double)> f, // [in] objective function to maximize
240 double leftEnd, // [in] smaller value of bracketing interval
241 double rightEnd, // [in] larger value of bracketing interval
242 double epsilon, // [in] stopping tolerance
243 double& maxLoc) // [out] location of maximum
244{
245 double result = minimize([&f](double x) -> double { return -1.0*f(x); },
246 leftEnd, rightEnd, epsilon, maxLoc);
247 return -1.0*result;
248}
249
250// Numerically integrate a given function f (that takes a
251// double argument to integrate over and returns a double)
252// over the interval [a,b] using Clenshaw-Curtis quadrature
253double marley_utils::num_integrate(const std::function<double(double)> &f,
254 double a, double b)
255{
258 static marley::Integrator integrator(50);
259 return integrator.num_integrate(f, a, b);
260}
261
262// Solves a quadratic equation of the form A*x^2 + B*x + C = 0
263// while attempting to minimize errors due to the inherent limitations
264// of floating-point arithmetic. The variables solPlus and solMinus
265// are loaded with the two solutions x = (-B ± sqrt(B^2 - 4*A*C))/(2*A).
266// Unsurprisingly, solPlus corresponds to the choice of the plus sign,
267// while solMinus corresponds to the choice of the minus sign.
268void marley_utils::solve_quadratic_equation(double A, double B,
269 double C, double &solPlus, double &solMinus) {
270
271 // Restructure the calculation to avoid some potentially bad cancellations
272 // See, e.g., http://www.petebecker.com/js/js200010.html for details
273 double c = C/A;
274 double b = B/(2*A);
275
276 double discr = b*b - c;
277
278 // Find both solutions of the quadratic equation while avoiding
279 // an extra subtraction (which can potentially lead to catastrophic
280 // loss of precision) between -b and the square root of the
281 // discriminant.
282 if (b > 0) {
283
284 solMinus = -b - marley_utils::real_sqrt(discr);
285 solPlus = c/solMinus;
286 }
287 else {
288
289 solPlus = -b + marley_utils::real_sqrt(discr);
290 solMinus = c/solPlus;
291 }
292
293}
294
295// Takes the square root of nonnegative arguments. Returns zero otherwise.
296// This sqrt implementation is a quick fix for cases where roundoff errors
297// give an argument to std::sqrt() that is slightly negative, causing it
298// to return NaN.
299
300// TODO: consider other ways of resolving this problem
301double marley_utils::real_sqrt(double num) {
302 if (num < 0) {
303 return 0;
304 }
305 else {
306 return std::sqrt(num);
307 }
308}
309
310// Prompts the user with a question that requires a yes/no answer. Returns
311// true if the answer was yes, or false if the answer was no.
312bool marley_utils::prompt_yes_no(const std::string& message) {
313 std::cout << '\n';
314 std::string response;
315 while (std::cout << message << " [y/n]? "
316 && std::getline(std::cin, response)
317 && !(response == "y" || response == "n" || response == "Y"
318 || response == "N"));
319 if (response == "y" || response == "Y") return true;
320 else return false;
321}
322
323// For a given atomic number Z and mass number A, return a matching ENSDF nucid
324std::string marley_utils::nuc_id(int Z, int A) {
325 // Check to make sure Z and A have acceptable values
326 if (Z < 0 || A < 1 || A > 999) throw marley::Error(
327 std::string("The atomic number Z = ") + std::to_string(Z)
328 + " and the mass number A = " + std::to_string(A)
329 + " do not correspond to a valid ENSDF nucid.");
330
331 // Create a three-character string representing the mass number
332 std::string atomic_mass_number;
333 if (A < 10) {
334 atomic_mass_number = " " + std::to_string(A);
335 }
336 else if (A < 100) {
337 atomic_mass_number = " " + std::to_string(A);
338 }
339 else {
340 atomic_mass_number = std::to_string(A);
341 }
342
343 // Get the element symbol as a string
344 std::string symbol = element_symbols.at(Z);
345
346 // Make the symbol completely uppercase
347 to_uppercase_inplace(symbol);
348
349 // If the symbol is only one character, pad the string so that
350 // it is two characters long
351 if (symbol.length() == 1) symbol += " ";
352
353 return atomic_mass_number + symbol;
354}
355
356// Function that takes a number of bytes and returns a string
357// representing the amount of memory in more readable units
358std::string marley_utils::num_bytes_to_string(double bytes,
359 unsigned precision)
360{
361 double divisor;
362 std::string SI_prefix;
363
364 if (bytes < 1e3) return std::to_string(static_cast<int>(bytes)) + " B";
365 else if (bytes < 1e6) {
366 divisor = 1e3;
367 SI_prefix = "k";
368 }
369 else if (bytes < 1e9) {
370 divisor = 1e6;
371 SI_prefix = "M";
372 }
373 else if (bytes < 1e12) {
374 divisor = 1e9;
375 SI_prefix = "G";
376 }
377 else {
378 divisor = 1e12;
379 SI_prefix = "T";
380 }
381
382 std::ostringstream out;
383 out << std::fixed;
384 out.precision(precision);
385 out << bytes/divisor << " " << SI_prefix << "B";
386 return out.str();
387}
388
389// This function exploits the observation (given in the first answer at
390// http://stackoverflow.com/questions/11062804/measuring-the-runtime-of-a-c-code)
391// that the difference of two std::chrono::system_clock::time_point objects
392// can be assigned to a std::chrono::system_clock::duration.
393//
394// Function that takes two std::system_clock::time_point objects and returns
395// a string (in the format days hours:minutes:seconds) representing the time
396// between them
397std::string marley_utils::elapsed_time_string(
398 std::chrono::system_clock::time_point &start_time,
399 std::chrono::system_clock::time_point &end_time)
400{
401 std::chrono::system_clock::duration time_elapsed
402 = end_time - start_time;
403
404 return marley_utils::duration_to_string
405 <std::chrono::system_clock::duration>(time_elapsed);
406}
407
408// Efficiently read in an entire file as a std::string
409// This function was taken from
410// http://insanecoding.blogspot.in/2011/11/how-to-read-in-file-in-c.html
411std::string marley_utils::get_file_contents(std::string filename) {
412
413 // Read from the file using a stream. Ignore windows line ending changes
414 std::ifstream in(filename.c_str(), std::ios::in | std::ios::binary);
415
416 if (in) {
417 std::string contents;
418
419 // Determine the size of the file. Allocate sufficient memory
420 // so that the string can hold the entire file without
421 // resizing itself
422 in.seekg(0, std::ios::end);
423 contents.resize(in.tellg());
424 in.seekg(0, std::ios::beg);
425
426 // Slurp in the entire file
427 in.read(&contents[0], contents.size());
428 in.close();
429 return contents;
430 }
431 throw marley::Error("Could not read from file " + filename);
432}
433
434// Advance to the next line of an ifstream that either matches (match == true)
435// or does not match (match == false) a given regular expression
436std::string marley_utils::get_next_line(std::ifstream &file_in,
437 const std::regex &rx, bool match)
438{
439
440 std::string line;
441 while (!file_in.eof() && file_in.good()) {
442 // Get the next line of the file
443 std::getline(file_in, line);
444
445 // Check to see if the new line fulfills the search criteria
446 if (std::regex_match(line, rx) == match) {
447 // If it does, return it
448 return line;
449 }
450 // If not, keep looking
451 }
452
453 // If the end of the file is encountered before a suitable
454 // line is found, return an empty string
455 return std::string("");
456}
457
458// Version of get_next_line that stores the number of lines
459// scanned into num_lines
460std::string marley_utils::get_next_line(std::ifstream &file_in,
461 const std::regex &rx, bool match, int& num_lines)
462{
463
464 num_lines = 0;
465
466 std::string line;
467 while (!file_in.eof() && file_in.good()) {
468 // Get the next line of the file
469 std::getline(file_in, line);
470
471 ++num_lines;
472
473 // Check to see if the new line fulfills the search criteria
474 if (std::regex_match(line, rx) == match) {
475 // If it does, return it
476 return line;
477 }
478 // If not, keep looking
479 }
480
481 // If the end of the file is encountered before a suitable
482 // line is found, return an empty string
483 return std::string("");
484}
485
490bool marley_utils::string_to_neutrino_pdg(const std::string& str, int& pdg) {
491 if (str == "ve") pdg = marley_utils::ELECTRON_NEUTRINO;
492 else if (str == "vebar") pdg = marley_utils::ELECTRON_ANTINEUTRINO;
493 else if (str == "vu") pdg = marley_utils::MUON_NEUTRINO;
494 else if (str == "vubar") pdg = marley_utils::MUON_ANTINEUTRINO;
495 else if (str == "vt") pdg = marley_utils::TAU_NEUTRINO;
496 else if (str == "vtbar") pdg = marley_utils::TAU_ANTINEUTRINO;
497 else {
498 pdg = 0;
499 return false;
500 }
501 return true;
502}
503
504std::string marley_utils::neutrino_pdg_to_string(int pdg) {
505 if (pdg == marley_utils::ELECTRON_NEUTRINO)
506 return std::string("ve");
507 else if (pdg == marley_utils::ELECTRON_ANTINEUTRINO)
508 return std::string("vebar");
509 else if (pdg == marley_utils::MUON_NEUTRINO)
510 return std::string("vu");
511 else if (pdg == marley_utils::MUON_ANTINEUTRINO)
512 return std::string("vubar");
513 else if (pdg == marley_utils::TAU_NEUTRINO)
514 return std::string("vt");
515 else if (pdg == marley_utils::TAU_ANTINEUTRINO)
516 return std::string("vtbar");
517 else return std::string("?");
518}
519
520// Trim an ENSDF nucid string and make two-letter element symbols have a
521// lowercase last letter. Currently, no checking is done to see if the
522// string is a valid nucid.
523std::string marley_utils::nucid_to_symbol(std::string nucid) {
524 if (nucid.length() != 5) {
525 // Split the string into "A" and "element name" pieces
526 std::smatch m;
527 std::regex_search(nucid, m, std::regex("[0-9]+"));
528 std::string z_str = m.str();
529 std::string e_str = m.suffix().str();
530
531 // If the element name has more than one letter,
532 // then make the last one lowercase.
533 if (e_str.length() > 1)
534 e_str.back() = tolower(e_str.back());
535 return z_str + e_str;
536 }
537 // The nucid is 5 characters long, so getting our
538 // desired format is a lot easier
539 nucid.back() = tolower(nucid.back());
540 return marley_utils::trim_copy(nucid);
541}
542
543// Assign a helicity value based on the PDG code and check its validity
544int marley_utils::get_particle_helicity( const int pdg ) {
545
546 // Unphysical, just used as a placeholder
547 constexpr int DUMMY_HELICITY = 0;
548 // Helicity value for antineutrinos (right-handed)
549 constexpr int RIGHT_HANDED = 1;
550 // Helicity value for neutrinos (left-handed)
551 constexpr int LEFT_HANDED = -1;
552
553 if ( pdg == marley_utils::ELECTRON_NEUTRINO
554 || pdg == marley_utils::MUON_NEUTRINO
555 || pdg == marley_utils::TAU_NEUTRINO )
556 {
557 // All Standard Model neutrinos are left-handed
558 return LEFT_HANDED;
559 }
560 else if ( pdg == marley_utils::ELECTRON_ANTINEUTRINO
561 || pdg == marley_utils::MUON_ANTINEUTRINO
562 || pdg == marley_utils::TAU_ANTINEUTRINO )
563 {
564 // All Standard Model antineutrinos are right-handed
565 return RIGHT_HANDED;
566 }
567 else throw marley::Error( "Handling of particles with PDG code = "
568 + std::to_string(pdg) + " is unimplemented in marley_utils::"
569 "get_particle_helicity()" );
570
571 return DUMMY_HELICITY;
572}
573
574std::string marley_utils::get_particle_symbol( int pid, bool excited ) {
575
576 // Try to find the particle in the hard-coded list
577 auto iter = particle_symbols.find( std::abs(pid) );
578
579 // If it was found, store the result
580 std::string result;
581 if ( iter != particle_symbols.end() ) {
582 result = iter->second;
583 // For charged leptons, indicate the sign. For antineutrinos,
584 // add a macron above the nu.
585 if ( is_lepton(pid) ) {
586 int charge = get_particle_charge( pid );
587 if ( charge < 0 ) result += "-";
588 else if ( charge > 0 ) result += "+";
589 else if ( pid < 0 ) {
590 static const std::string COMBINING_MACRON( "̄" );
591 result = result.substr( 0, 2 ) + COMBINING_MACRON + result.back();
592 }
593 }
594 }
595 // Otherwise, check if it is a complex nucleus (or ion). If it is, follow
596 // the standard recipe using Z and A.
597 else if ( marley_utils::is_ion(pid) ) {
598 result = std::to_string( get_particle_A(pid) );
599 int Z = get_particle_Z( pid );
600 auto el_iter = marley_utils::element_symbols.find( Z );
601 if ( el_iter != element_symbols.end() ) {
602 result += el_iter->second;
603 if ( excited ) result += '*';
604 return result;
605 }
606 else {
607 throw marley::Error( "Unrecognized element with Z = "
608 + std::to_string( Z ) + " encountered in marley_utils::"
609 "get_particle_symbol()" );
610 }
611 } // is ion or complex nucleus
612 else throw marley::Error( "Unrecognized PDG code " + std::to_string(pid) );
613
614 // Only PDG codes corresponding to complex nuclei should use the excited flag
615 if ( excited && !is_ion(pid) ) {
616 throw marley::Error( "Excited flag set for PDG code = "
617 + std::to_string( pid ) + " in marley_utils::get_particle_symbol()" );
618 }
619
620 return result;
621}
622
623// Converts an ENSDF nucid to an atomic number. Currently, no checking is done
624// to see if the string is a valid nucid.
625int marley_utils::nucid_to_Z(std::string nucid) {
626 // String that will be loaded with the element symbol
627 std::string e_str;
628
629 if (nucid.length() != 5) {
630 // Extract the element name from the string
631 std::smatch m;
632 std::regex_search(nucid, m, std::regex("[0-9]+"));
633 e_str = m.suffix().str();
634
635 // If the element name has more than one letter,
636 // then make the last one lowercase.
637 if (e_str.length() > 1)
638 e_str.back() = tolower(e_str.back());
639 }
640 else {
641 // The nucid is 5 characters long, so getting our
642 // desired format is a lot easier
643 nucid.back() = tolower(nucid.back());
644 e_str = nucid.substr(nucid.size() - 2); // Get the last two characters
645 marley_utils::trim_right_inplace(e_str); // Trims the string if needed
646 }
647 return atomic_numbers.at(e_str);
648}
649
650 // Lookup table for element symbols (keys are atomic numbers Z,
651 // values are symbols on the periodic table). The symbol "Nn" is
652 // used for a neutron to match the ENSDF convention.
653 const std::unordered_map<int, std::string> marley_utils::element_symbols = {
654 { 0, "Nn"},
655 { 1, "H" },
656 { 2, "He" },
657 { 3, "Li" },
658 { 4, "Be" },
659 { 5, "B" },
660 { 6, "C" },
661 { 7, "N" },
662 { 8, "O" },
663 { 9, "F" },
664 { 10, "Ne" },
665 { 11, "Na" },
666 { 12, "Mg" },
667 { 13, "Al" },
668 { 14, "Si" },
669 { 15, "P" },
670 { 16, "S" },
671 { 17, "Cl" },
672 { 18, "Ar" },
673 { 19, "K" },
674 { 20, "Ca" },
675 { 21, "Sc" },
676 { 22, "Ti" },
677 { 23, "V" },
678 { 24, "Cr" },
679 { 25, "Mn" },
680 { 26, "Fe" },
681 { 27, "Co" },
682 { 28, "Ni" },
683 { 29, "Cu" },
684 { 30, "Zn" },
685 { 31, "Ga" },
686 { 32, "Ge" },
687 { 33, "As" },
688 { 34, "Se" },
689 { 35, "Br" },
690 { 36, "Kr" },
691 { 37, "Rb" },
692 { 38, "Sr" },
693 { 39, "Y" },
694 { 40, "Zr" },
695 { 41, "Nb" },
696 { 42, "Mo" },
697 { 43, "Tc" },
698 { 44, "Ru" },
699 { 45, "Rh" },
700 { 46, "Pd" },
701 { 47, "Ag" },
702 { 48, "Cd" },
703 { 49, "In" },
704 { 50, "Sn" },
705 { 51, "Sb" },
706 { 52, "Te" },
707 { 53, "I" },
708 { 54, "Xe" },
709 { 55, "Cs" },
710 { 56, "Ba" },
711 { 57, "La" },
712 { 58, "Ce" },
713 { 59, "Pr" },
714 { 60, "Nd" },
715 { 61, "Pm" },
716 { 62, "Sm" },
717 { 63, "Eu" },
718 { 64, "Gd" },
719 { 65, "Tb" },
720 { 66, "Dy" },
721 { 67, "Ho" },
722 { 68, "Er" },
723 { 69, "Tm" },
724 { 70, "Yb" },
725 { 71, "Lu" },
726 { 72, "Hf" },
727 { 73, "Ta" },
728 { 74, "W" },
729 { 75, "Re" },
730 { 76, "Os" },
731 { 77, "Ir" },
732 { 78, "Pt" },
733 { 79, "Au" },
734 { 80, "Hg" },
735 { 81, "Tl" },
736 { 82, "Pb" },
737 { 83, "Bi" },
738 { 84, "Po" },
739 { 85, "At" },
740 { 86, "Rn" },
741 { 87, "Fr" },
742 { 88, "Ra" },
743 { 89, "Ac" },
744 { 90, "Th" },
745 { 91, "Pa" },
746 { 92, "U" },
747 { 93, "Np" },
748 { 94, "Pu" },
749 { 95, "Am" },
750 { 96, "Cm" },
751 { 97, "Bk" },
752 { 98, "Cf" },
753 { 99, "Es" },
754 { 100, "Fm" },
755 { 101, "Md" },
756 { 102, "No" },
757 { 103, "Lr" },
758 { 104, "Rf" },
759 { 105, "Db" },
760 { 106, "Sg" },
761 { 107, "Bh" },
762 { 108, "Hs" },
763 { 109, "Mt" },
764 { 110, "Ds" },
765 { 111, "Rg" },
766 { 112, "Cn" },
767 //{ 113, "Uut" },
768 { 114, "Fl" },
769 //{ 115, "Uup" },
770 { 116, "Lv" },
771 //{ 117, "Uus" },
772 //{ 118, "Uuo" },
773 };
774
775 // TODO: consider other ways of adding support for fast reverse lookups
776 // in the element_symbols map rather than reproducing its contents here
777 const std::unordered_map<std::string, int> marley_utils::atomic_numbers = {
778 {"Nn", 0 },
779 {"H" , 1 },
780 {"He", 2 },
781 {"Li", 3 },
782 {"Be", 4 },
783 {"B" , 5 },
784 {"C" , 6 },
785 {"N" , 7 },
786 {"O" , 8 },
787 {"F" , 9 },
788 {"Ne", 10 },
789 {"Na", 11 },
790 {"Mg", 12 },
791 {"Al", 13 },
792 {"Si", 14 },
793 {"P" , 15 },
794 {"S" , 16 },
795 {"Cl", 17 },
796 {"Ar", 18 },
797 {"K" , 19 },
798 {"Ca", 20 },
799 {"Sc", 21 },
800 {"Ti", 22 },
801 {"V" , 23 },
802 {"Cr", 24 },
803 {"Mn", 25 },
804 {"Fe", 26 },
805 {"Co", 27 },
806 {"Ni", 28 },
807 {"Cu", 29 },
808 {"Zn", 30 },
809 {"Ga", 31 },
810 {"Ge", 32 },
811 {"As", 33 },
812 {"Se", 34 },
813 {"Br", 35 },
814 {"Kr", 36 },
815 {"Rb", 37 },
816 {"Sr", 38 },
817 {"Y" , 39 },
818 {"Zr", 40 },
819 {"Nb", 41 },
820 {"Mo", 42 },
821 {"Tc", 43 },
822 {"Ru", 44 },
823 {"Rh", 45 },
824 {"Pd", 46 },
825 {"Ag", 47 },
826 {"Cd", 48 },
827 {"In", 49 },
828 {"Sn", 50 },
829 {"Sb", 51 },
830 {"Te", 52 },
831 {"I" , 53 },
832 {"Xe", 54 },
833 {"Cs", 55 },
834 {"Ba", 56 },
835 {"La", 57 },
836 {"Ce", 58 },
837 {"Pr", 59 },
838 {"Nd", 60 },
839 {"Pm", 61 },
840 {"Sm", 62 },
841 {"Eu", 63 },
842 {"Gd", 64 },
843 {"Tb", 65 },
844 {"Dy", 66 },
845 {"Ho", 67 },
846 {"Er", 68 },
847 {"Tm", 69 },
848 {"Yb", 70 },
849 {"Lu", 71 },
850 {"Hf", 72 },
851 {"Ta", 73 },
852 {"W" , 74 },
853 {"Re", 75 },
854 {"Os", 76 },
855 {"Ir", 77 },
856 {"Pt", 78 },
857 {"Au", 79 },
858 {"Hg", 80 },
859 {"Tl", 81 },
860 {"Pb", 82 },
861 {"Bi", 83 },
862 {"Po", 84 },
863 {"At", 85 },
864 {"Rn", 86 },
865 {"Fr", 87 },
866 {"Ra", 88 },
867 {"Ac", 89 },
868 {"Th", 90 },
869 {"Pa", 91 },
870 {"U" , 92 },
871 {"Np", 93 },
872 {"Pu", 94 },
873 {"Am", 95 },
874 {"Cm", 96 },
875 {"Bk", 97 },
876 {"Cf", 98 },
877 {"Es", 99 },
878 {"Fm", 100 },
879 {"Md", 101 },
880 {"No", 102 },
881 {"Lr", 103 },
882 {"Rf", 104 },
883 {"Db", 105 },
884 {"Sg", 106 },
885 {"Bh", 107 },
886 {"Hs", 108 },
887 {"Mt", 109 },
888 {"Ds", 110 },
889 {"Rg", 111 },
890 {"Cn", 112 },
891 //{ "Uut", 113 },
892 {"Fl", 114 },
893 //{ "Uup", 115 },
894 {"Lv", 116 },
895 //{ "Uus", 117 },
896 //{ "Uuo", 118 },
897 };
898
899 const std::string marley_utils::marley_logo = "╔╦╗╔═╗╦═╗╦ ╔═╗╦ ╦\n"
900 "║║║╠═╣╠╦╝║ ║╣ ╚╦╝\n"
901 "╩ ╩╩ ╩╩╚═╩═╝╚═╝ ╩\n";
902
903 const std::string marley_utils::marley_pic =
904 "ZZ77I\?I7777\?\?+I\?\?+=====~=~~~:+=+7I$I$Z$$O"
905 "Z77Z$O\?\?=\?\?$Z$Z=$=~=~,:::~~:=~===+=+=II\n"
906 "Z$7IIIIII+I=++=+==+~=~~~~~===\?ZIZ$OOZZOOI8$ZZ"
907 "O8Z8O$ZOO7Z+:,:::,::~:::::~+~:~=~=+\n"
908 "$77I$II\?+I\?+++=+=+====+\?=~=I$7O7$OOZ$8OO8DO"
909 "O8888O8ZOZZ8Z7I~:::::::::::~~:~==~==~\n"
910 "I7I$I7++\?+++=+=++===~~++I\?OO$I$ZOZDO8O888D88"
911 "8OO8888888ZOO$\?I::~,::,::::~:~~=~~==\n"
912 "IIII+\?\?I\?=\?+=+=====~~=$$Z$$Z8IOZOZOD888D$="
913 ":::,,,,,:Z88888ZZ$=\?:,~::::~~:~,:~~~:~\n"
914 "77I$\?\?\?+\?+\?=+=~=~~~=7Z87ZZ$OIZZOO8888DD\?"
915 "::::,,::::,,::O88O8ZZ=:=:::,::::::::=~:=\n"
916 "II+\?+\?\?++++======++7Z8I7OO$$DO$8Z888DZ\?~~:"
917 ",,,,,,,,,:,:~:$O8ZOZ\?=~,:,,:,:::::~~~:\n"
918 "$+I\?+\?+=+===+=+\?+IZ8$DDDIZO8OOD8D88D87+:::="
919 "7ZI7~,,,,:::::~8O8ZZ7+~::,:,::,:,:::~\n"
920 "7+\?+\?+\?+======\?$OO$IOD8OD88O8OD7D8DD8I:~77"
921 "$7ZZ$$O+::,:::~~=8O78OI\?~:,:,::,,,::~:\n"
922 "I\?+++=\?+=~++$$ZZODZ88ZDDOZZDOZD8DDDD7~~Z\?=~"
923 "==~Z88O+~,::::~~=88OO8$+~=:,,::,,,:,:\n"
924 "+++++==++I$$ZO8$$O8DNDNDOZ8O88O8DDD$+\?$$8O$88"
925 "8O8O$7:~~:~,,~~O8888OO$~,:,,:::,:,:\n"
926 "+\?++====\?\?$O88Z88ODD$ZOZ$$OOOOODDD\?~~::~=$"
927 "Z~Z7887I~::$Z$O\?::I888ZO8$\?~:,,::~::,,\n"
928 "\?+++====\?$88DDZ$88OZDDD8$O88O88D8\?:::,:,,:+"
929 "8OO+7$~,,,O8OOOZ==8888888O\?=~::::::,,\n"
930 "++=====+\?OODZ$$DD8DDD8ZZ$$ODO8DD=::~,,:::,,,,"
931 ":,,,,:,,:OI~:ZOZ88D8$8OO7+:,::::~::\n"
932 "+++=+=\?\?8D87DZ8DDDDZ888O88OO88ZD\?\?\?:::~::"
933 ":~=I::,,,:~,,88D:,7Z8D88O88O+=:,,,:~::,\n"
934 "+=+=++78Z7OZZ8DDDD8DDD8$+78$8D8D7I~:~I\?II7$I\?"
935 "ZI$DD8I\?+==~88II8DD8O88OZ~:,,:::,:,\n"
936 "++===+ID$ODDDDNDDDDD7=$O\?$88NDD8I\?++I\?+77\?"
937 "~~~:=\?IOZ$OO7O8787$8D88$88ZO=~::,:::::\n"
938 "=+===+I$8D8=8NDDND=OZZD$\?7ODO8D87=\?++II+Z++\?"
939 "I\?Z7$Z$I$D8~::~Z7DDD87O$OZ++,,,::::,\n"
940 "===~+\?8DZ8N8DNDNNDD+I=77I7D8DDDOZ++==\?I8\?\?"
941 "$8ZO$7=\?$\?Z$==::,:8DDD8888O$$~,,:::::,\n"
942 "====$8OOODN\?DDNDNNDI=+ZIIZD8DDD$7+I+==IZ8Z~~\?"
943 "~O8$7\?=ZI+$:::~DDDD8O88$87~,::::::,\n"
944 "==+8$8O88N8ODNNNDNDND7Z=ZDD8NDNO+7+=\?+\?~Z\?,"
945 ":,,+,+DDZ7Z+7~~,~DDDD888O8$$\?:,,,:::,\n"
946 "+~=+ZO88DD8DNNDNDDND78DI\?O8DDDDZ\?7I=\?==~~ZD"
947 "DDDO,:,=D87+I==~=DDDD8DDOO$I=::,::::,\n"
948 "+=+\?8I8DDNO8NNNNDNNDDD\?IDON8DDD$7$I\?7=~=~~~"
949 ":~8DDD.~888O7I\?\?8DDD88OD8O$~:::,,:,:,\n"
950 "=~\?8$O8DDDZDNDNNDNDNDD78DDNNDDDZI+\?\?7~=+:=="
951 "77~~:ZO,DOOZZ7$ZDDDD8D8D888$::::,::,,\n"
952 "+=\?8IDDNDDODNNNNNNNN$D$DDDNDDDDO$=7=+=~\?+==\?"
953 "+7Z7I7$I\?Z$$ODDDDD88DDDD88I\?,,::,:,:\n"
954 "=\?$OI8NDND8DNNDNNNDD7788DNDD8DDNZ$I+\?~~~==+="
955 "$$$II$I~ZIOO8DDDDD8D8D8DDO7=::,:,,:,\n"
956 "I$8888DDNDDDNN8NNNNN7Z88NNDDNNDDD8ZO7I=~~:~\?="
957 "77II\?\?7O$ZODDDDD8DDD8O88Z\?:,:::,::,\n"
958 "$DDZ8DDNND8DNDNNNNND7$8ODN8DNND888D8$$ZI$$\?I~"
959 "=+I\?+7Z7$ODDDNDD88888OODO:::,,,,:::\n"
960 "8DDOD8DDNN$ND7NNNNND8I88NN8DDNDDDZDD$ZZIZ88ZO7"
961 "\?==78$IZDDDDDDDOD888DD8Z,::,,,,::,\n"
962 "DD8DDDDDDNNNO\?NNNNN\?878NNNZOZDDD8ZDNDD888O8D"
963 "OD87ZOI\?O8DDDDDDDODO$D7OO~:::,:,::::\n"
964 "8NZOO8D8NDNNN7D88NNIOID8ND88$NDD8O$DDDDDN88Z8D"
965 "8DD8Z7D8DDDDDDDOD8\?8OO=::,:::,:,,,\n"
966 "DOZ8ZD8DDDNDDDN$8ONDNI88NNZD7DND$$8DNDNND8DNOO"
967 "$NODODDDNDDDNDDD887IOO=:::::::,,,:\n"
968 "NID8ND8DDNN+DNN$DID8D=ODDNZD\?8DN8ODZDDDN8NDOD"
969 "8DDD8DDDDDDDDNDDD8~IDOO~:::,:,:,:,:\n"
970 "IDODDDNDNDNZNNNI77D8+IZDN888=O8DD8DDNDNNNN8ODD"
971 "DDN8DDDDDDDDDZDZI77+OO$I~:::::,:,:\n"
972 "7NODDDD8NNDDN\?I7$INO++ODDI8D+7O8$88NNNDDNNDDD"
973 "D8D8O$DDDDDND88O$$887$\?OZZ:::,,::,,\n"
974 "NNDODDNDDNDNO$7I\?+NZ\?7DD$88=+\?ZZ$OIDDNNDND8"
975 "DODND8$=DDDNDD8OOI$O87$O8Z8\?+:~::,,::\n"
976 "NNNDZD$DN+DNOZZ\?==D7=IDN$DNI=I$ZZZ7$Z$DDNN8D8"
977 "O$8$::DDNN8D8D8I8O8Z88DD8I\?+:::,,::\n"
978 "NNNZ$DDNNZ7NOII+==D8+ZO=8D\?++I$$Z$88O8OZ8OZOI"
979 "Z8$::NNDNDOD88$$ZIIIZOD88I8O+:::,,,\n"
980 "NNNZ8DDND8ON7ZI==~DOZ88OO7$I\?\?7+I$\?$Z8$$Z$Z"
981 "ZZD+::ODNZOD8=7I$D8Z$~I8DDD888+~~,,,,\n"
982 "NNN$DD8NDDNN+7I==~7\?+7ZZ8I7I\?7\?77I7+ZZZ$$7Z"
983 "NI~::~88O8DD+O$$DODZZZZDDODOO+:$\?:~,,\n"
984 "NNDD8DNN8ZDN8I\?~~=~Z\?$I8D$7I=\?ZZ77\?\?$II$Z"
985 "DZ=~::8D8ODZINZO$ODDI78=8DD8D+\?Z$$Z\?::,\n"
986 "NN\?\?NNNDD8DD$\?7=NN=OZD8ODZ\?I=7ZI$I7O$$OO$="
987 "DDI:~\?7OOIDD8\?$DDDO\?Z\?OZ87I8~OZ888Z+~:\n"
988 "NNIO8NND88NN8IIND87ZDDO8$I\?7$ZIII7\?\?Z8:=\?+"
989 "OZD:IIDIODND7\?O$NDOOI7::O$\?77DZO$D8ZI~\n"
990 "NNI7DDNNDONNO\?O$ONDNI\?DN$+\?ZIZ$7I778=++==++"
991 "ID\?Z$O$ZDD\?\?7$DDO\?$$O~:8+~OO8$\?888OZ=\n"
992 "NNO78NNN78NNDDZ7DN$8DZ78$I7I+$I7\?\?Z~+++=++\?"
993 "II$OOIIZDDD87ODD\?ZO8Z:8Z$7ODDZ78D8O$\?\n"
994 "NN=ZONNDDODD77$ZN7D8D=NOO$$I$7I77$8+\?++\?=+\?"
995 "$7I\?DO7DDDD87ND8Z8$8~:=OO7Z$OI+8DDZZ\?\n"
996 "NDOO8NND$ON87NDD\?7\?$D7N8Z7II8ZI+Z8~\?==+==I\?"
997 "DZID$=ZDD8ZIZNODOZ8D~:~ZD$\?\?\?D7O8DOI\?\n"
998 "NNIODNNNDNNI$NO\?+DDNDD8ZZ$78OO$ID~\?+\?=++=IZ"
999 "$OO8OI$N$$Z8NDZZO$D::~=$OZI+=$$$+O8Z+\n"
1000 "NI\?O8NNNDD\?ODD8=$$887DOZZ$I7\?\?\?$~+\?=+~=\?"
1001 "\?~O\?ZZN\?I7\?OZ8NDOOIDZI:~==+$OI~~~ZI~78ZI\n"
1002 "NZ7DNNNNN8DNNOD\?78N8N8$\?7I\?\?++7==+\?\?~~=+"
1003 "+Z~OZD7Z=DO7NND7$78DO=~====O$+:~=OZ=\?$O+\n"
1004 "N8OZDNNNNZNMN7$\?8OO\?OZ\?7$7III7$+++=====++=8"
1005 "+$ZI7\?$7$8D$D\?ODD8O==~=IO8\?\?+=D$I=7I=\n"
1006 "ND88ODN8NNNNN7\?Z7ZN=D8I$\?\?\?I+7~\?\?=++===+"
1007 "Z8\?+I$\?O\?DD8+8\?DOO8DN7I+==+=Z8=Z~$Z7+\?7\?\n";
Base class for all exceptions thrown by MARLEY functions.
Definition Error.hh:26
Numerical integrator that uses Clenshaw-Curtis quadrature
Definition Integrator.hh:28