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
MassTable.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// MARLEY includes
18#include "marley/Error.hh"
19#include "marley/Fragment.hh"
20#include "marley/FileManager.hh"
21#include "marley/JSON.hh"
22#include "marley/Logger.hh"
23#include "marley/MassTable.hh"
24#include "marley/StructureDatabase.hh"
25#include "marley/marley_utils.hh"
26
27// Initialize the static data file name
28const std::string marley::MassTable::data_file_name_ = "mass_table.js";
29
31
32 // Instantiate the file manager and use it to find
33 // the mass table data file
34 const auto& fm = marley::FileManager::Instance();
35 std::string full_mt_file_name
36 = fm.find_file( data_file_name_ );
37
38 if ( full_mt_file_name.empty() ) {
39 throw marley::Error("Could not find the MARLEY mass table"
40 " data file " + data_file_name_ + ". Please ensure that"
41 " the folder containing it is on the MARLEY search path."
42 " If needed, the folder can be appended to the MARLEY_SEARCH_PATH"
43 " environment variable.");
44 }
45
46 MARLEY_LOG( INFO, "init.structure" ) << "Loading particle and atomic masses from "
47 << full_mt_file_name;
48
49 // Read in the mass table from a JSON data file
50 auto json_table = marley::JSON::load_file( full_mt_file_name );
51
52 // Store the JSON entries in the relevant unordered maps
53 if ( !json_table.has_key("particle_masses") ) {
54 throw marley::Error("Problem reading the mass table data file "
55 + data_file_name_ + ". Missing \"particle_masses\" JSON array.");
56 }
57 const auto& pm_json = json_table.at("particle_masses");
58 assign_masses( pm_json, "particle_masses", this->particle_masses_ );
59
60 if ( !json_table.has_key("atomic_masses") ) {
61 throw marley::Error("Problem reading the mass table data file "
62 + data_file_name_ + ". Missing \"atomic_masses\" JSON array.");
63 }
64 const auto& am_json = json_table.at("atomic_masses");
65 assign_masses( am_json, "atomic_masses", this->atomic_masses_ );
66
67}
68
70
71 // Create the mass table using a static variable. This ensures
72 // that the singleton instance is only created once.
73 static std::unique_ptr<marley::MassTable>
74 the_instance(new marley::MassTable());
75
76 // Return a reference to the singleton instance
77 return *the_instance;
78}
79
81 return liquid_drop_model_mass_excess(Z, A) + micro_amu_*1e6*A;
82}
83
84double marley::MassTable::get_particle_mass(int particle_id) const {
85 int id = particle_id;
86 // The lookup table only includes entries for particles (as opposed to
87 // antiparticles), so flip the sign of the input particle id for the
88 // lookup if it represents an antiparticle.
89 if (id < 0) id *= -1;
90 // Find the particle's mass in the lookup table, and convert its
91 // value from micro-amu to MeV
92 return micro_amu_ * particle_masses_.at(id);
93}
94
95double marley::MassTable::get_atomic_mass(int nucleus_pid, bool theory_ok) const
96{
97 bool exp;
98 double mass = lookup_atomic_mass(nucleus_pid, exp,
99 theory_ok);
100 if (exp) mass *= micro_amu_;
101 return mass;
102}
103
104double marley::MassTable::lookup_atomic_mass(int nucleus_pid, bool& exp,
105 bool theory_ok) const
106{
107 // Find the atom's mass (in micro-amu) in the lookup table using its
108 // nucleus's particle ID number. If it can't be found, either return a
109 // theoretical mass using the liquid drop model or throw an error.
110 // std::unordered_map<int, double>::iterator search
111 auto search = atomic_masses_.find(nucleus_pid);
112
113 // If the mass was found in the lookup table, return it and flag it as an
114 // experimental value
115 if (search != atomic_masses_.end()) {
116 exp = true;
117 return search->second;
118 }
119 // Otherwise, return a theoretical estimate using the liquid drop model or
120 // throw an error depending on whether the user has indicated that using a
121 // theoretical estimate is acceptable. For either case, set the experimental
122 // flag to false.
123 else {
124 exp = false;
125
126 int Z = marley_utils::get_particle_Z(nucleus_pid);
127 int A = marley_utils::get_particle_A(nucleus_pid);
128
129 if (theory_ok) {
130 return liquid_drop_model_atomic_mass(Z, A);
131 }
132 else throw marley::Error(std::string("Entry for Z = ")
133 + std::to_string(Z) + " and A = " + std::to_string(A)
134 + " not found in the MARLEY atomic mass table.");
135 }
136}
137
138double marley::MassTable::lookup_atomic_mass(int Z, int A, bool& exp,
139 bool theory_ok) const
140{
141 int nucleus_pid = marley_utils::get_nucleus_pid(Z, A);
142
143 auto search = atomic_masses_.find(nucleus_pid);
144
145 // If the mass was found in the lookup table, return it and flag it as an
146 // experimental value
147 if (search != atomic_masses_.end()) {
148 exp = true;
149 return search->second;
150 }
151 // Otherwise, return a theoretical estimate using the liquid drop model or
152 // throw an error depending on whether the user has indicated that using a
153 // theoretical estimate is acceptable. For either case, set the experimental
154 // flag to false.
155 else {
156 exp = false;
157
158 if (theory_ok) {
159 return liquid_drop_model_atomic_mass(Z, A);
160 }
161 else throw marley::Error(std::string("Entry for Z = ")
162 + std::to_string(Z) + " and A = " + std::to_string(A)
163 + " not found in the MARLEY atomic mass table.");
164 }
165}
166
167double marley::MassTable::get_binding_energy(int Z, int A, bool theory_ok) const
168{
169 int N = A - Z;
170 double m_hydrogen_1 = atomic_masses_.at(1000010010);
171 double mn = particle_masses_.at(marley_utils::NEUTRON);
172
173 bool exp;
174 double mN = lookup_atomic_mass(Z, A, exp, theory_ok);
175
176 // Experimental masses are given in micro-amu, while our liquid drop model
177 // estimates are given in MeV, so adjust the calculation appropriately
178 // depending on which unit we are using for mN.
179 if (exp) return micro_amu_ * (Z*m_hydrogen_1 + N*mn - mN);
180 else return micro_amu_ * (Z*m_hydrogen_1 + N*mn) - mN;
181}
182
183double marley::MassTable::get_mass_excess(int Z, int A, bool theory_ok) const
184{
185 bool exp;
186 double mN = lookup_atomic_mass(Z, A, exp, theory_ok);
187 if (exp) return micro_amu_*(mN - A*1e6);
188 else return mN - micro_amu_*A*1e6;
189}
190
191double marley::MassTable::get_atomic_mass(int Z, int A, bool theory_ok) const {
192 bool exp;
193 double mass = lookup_atomic_mass(Z, A, exp, theory_ok);
194 if (exp) mass *= micro_amu_;
195 return mass;
196}
197
199 int frag_pdg, bool theory_ok) const
200{
201 int Zi = marley_utils::get_particle_Z( nuc_pdg );
202 int Ai = marley_utils::get_particle_A( nuc_pdg );
203 return this->get_fragment_separation_energy( Zi, Ai, frag_pdg, theory_ok );
204}
205
207 bool theory_ok) const
208{
209 int Zx = marley_utils::get_particle_Z(pid);
210 int Zf = Z - Zx;
211 int Af = A - marley_utils::get_particle_A(pid);
212
213 // This value is in micro-atomic-mass-units (micro-AMU)
214 double extra_mass = Zx*particle_masses_.at(marley_utils::ELECTRON)
215 + particle_masses_.at(pid);
216
217 // These values may be in micro-AMU or MeV
218 bool exp_i, exp_f;
219 double m_atom_initial = lookup_atomic_mass(Z, A, exp_i, theory_ok);
220 double m_atom_final = lookup_atomic_mass(Zf, Af, exp_f, theory_ok);
221
222 // Experimental values are tabulated in micro-AMU, while theoretical
223 // estimates use MeV. Adjust application of the conversion factor
224 // appropriately for each case so that we always return something in MeV.
225 if (exp_i) {
226 if (exp_f) return micro_amu_*(m_atom_final - m_atom_initial + extra_mass);
227 else return m_atom_final + micro_amu_*(extra_mass - m_atom_initial);
228 }
229 else if (exp_f) return micro_amu_*(m_atom_final + extra_mass)
230 - m_atom_initial;
231 else return micro_amu_*extra_mass + m_atom_final - m_atom_initial;
232}
233
239
240 // Liquid drop model parameters (taken from paper by A. J. Koning, et al.)
241 static constexpr double Mn = 8.07144; // MeV
242 static constexpr double MH = 7.28899; // MeV
243 static constexpr double a1 = 15.677; // MeV
244 static constexpr double a2 = 18.56; // MeV
245 static constexpr double kappa = 1.79;
246 static constexpr double c3 = 0.717; // MeV
247 static constexpr double c4 = 1.21129; // MeV
248
249 int N = A - Z;
250
251 double kappa_term = kappa * std::pow((N - Z) / static_cast<double>(A), 2);
252 double c1 = a1 * (1 - kappa_term);
253 double c2 = a2 * (1 - kappa_term);
254
255 double Evol = -c1 * A;
256 double Esur = c2 * std::pow(A, 2.0/3.0);
257 double Ecoul = (c3 / std::pow(A, 1.0/3.0) - c4 / A) * std::pow(Z, 2);
258
259 double delta_LDM = 0;
260 bool z_odd = Z % 2;
261 bool n_odd = N % 2;
262
263 // delta_LDM will be zero if the nucleus is odd-even
264 if (z_odd && n_odd) delta_LDM = 11/std::sqrt(A);
265 else if (!z_odd && !n_odd) delta_LDM = -11/std::sqrt(A);
266
267 return Mn * N + MH * Z + Evol + Esur + Ecoul + delta_LDM;
268}
269
271 const int Ai, const marley::Fragment& f) const
272{
273 // Separation energy for the fragment
274 double Sa = this->get_fragment_separation_energy( Zi, Ai, f.get_pid() );
275 return Sa;
276}
277
278// Returns the smallest nuclear fragment emission threshold (separation energy)
279// for a particular initial nucleus. All nuclear fragments recognized by the
280// StructureDatabase class are considered.
281double marley::MassTable::unbound_threshold(const int Zi, const int Ai) const
282{
283 // Before looking up the separation energies, start by setting the unbound
284 // threshold to infinity.
285 double unbound_threshold = std::numeric_limits<double>::max();
286 MARLEY_LOG( TRACE, "init.structure.masstable" ) << "unbound_threshold = " << unbound_threshold << '\n';
287
288 // Loop over each available nuclear fragment. If it has a smaller separation
289 // energy than the current value of unbound_threshold, update the stored value
290 for ( const auto& pair : marley::StructureDatabase::fragments() ) {
291 const marley::Fragment& f = pair.second;
292 double thresh = this->fragment_emission_threshold( Zi, Ai, f );
293 MARLEY_LOG( TRACE, "init.structure.masstable" ) << f.get_pid()
294 << " emission threshold = " << thresh << '\n';
295 if ( thresh < unbound_threshold ) unbound_threshold = thresh;
296 MARLEY_LOG( TRACE, "init.structure.masstable" ) << "unbound_threshold = "
297 << unbound_threshold << '\n';
298 }
299
300 return unbound_threshold;
301}
302
303double marley::MassTable::unbound_threshold(const int initial_nucleus_pdg) const
304{
305 int Zi = marley_utils::get_particle_Z( initial_nucleus_pdg );
306 int Ai = marley_utils::get_particle_A( initial_nucleus_pdg );
307 return this->unbound_threshold( Zi, Ai );
308}
309
310void marley::MassTable::assign_masses(const marley::JSON& obj_array,
311 const std::string& array_key, std::unordered_map<int, double>& map_to_use)
312{
313 if ( !obj_array.is_array() ) {
314 throw marley::Error("The \"" + array_key + "\" key in the"
315 " mass data file " + data_file_name_
316 + " does not refer to a JSON array.");
317 }
318 auto elements = obj_array.array_range();
319 if ( elements.begin() == elements.end() ) {
320 throw marley::Error("The \"" + array_key + "\" array in"
321 " the mass data file " + data_file_name_ + " is empty.");
322 }
323 bool pdg_ok, mass_ok;
324 for (const auto& el : elements) {
325
326 if ( !el.has_key("pdg") ) {
327 throw marley::Error("Missing pdg code for an element of the"
328 " \"" + array_key + "\" array in the mass data file " + data_file_name_);
329 }
330 else if ( !el.has_key("mass") ) {
331 throw marley::Error("Missing mass for an element of the"
332 " \"" + array_key + "\" array in the mass data file " + data_file_name_);
333 }
334
335 auto pdg_json = el.at("pdg");
336 int pdg = pdg_json.to_long( pdg_ok );
337 if ( !pdg_ok ) throw marley::Error(std::string("Invalid PDG code \"")
338 + pdg_json.dump_string() + "\" given in the \"" + array_key + "\" array"
339 " in the mass data file " + data_file_name_);
340
341 auto mass_json = el.at("mass");
342 double mass = mass_json.to_double( mass_ok );
343 if ( !mass_ok ) throw marley::Error(std::string("Invalid mass value \"")
344 + mass_json.dump_string() + "\" given in the \"" + array_key + "\" array"
345 " in the mass data file " + data_file_name_);
346
347 map_to_use[ pdg ] = mass;
348 MARLEY_LOG( TRACE, "init.structure.masstable" ) << array_key
349 << ": PDG " << pdg << " -> mass = " << mass << " MeV";
350 }
351
352}
Base class for all exceptions thrown by MARLEY functions.
Definition Error.hh:26
static const FileManager & Instance()
Get a const reference to the singleton instance of the FileManager.
Simple container for storing reference data about each of the nuclear fragments considered by MARLEY'...
Definition Fragment.hh:27
int get_pid() const
Get the PDG particle ID for this fragment.
Definition Fragment.hh:73
Singleton lookup table for particle and atomic masses.
Definition MassTable.hh:30
double fragment_emission_threshold(const int Zi, const int Ai, const marley::Fragment &f) const
Get the approximate excitation energy threshold for emission of a particular nuclear fragment.
Definition MassTable.cc:270
double get_fragment_separation_energy(int Z, int A, int pdg, bool theory_ok=true) const
Get the separation energy for emission of a nuclear fragment from a nucleus.
Definition MassTable.cc:206
static const MassTable & Instance()
Get a const reference to the singleton instance of the MassTable.
Definition MassTable.cc:69
double get_mass_excess(int Z, int A, bool theory_ok=true) const
Get the mass excess of a nucleus.
Definition MassTable.cc:183
double get_atomic_mass(int pdg_code, bool theory_ok=true) const
Get the mass of an atom.
Definition MassTable.cc:95
double liquid_drop_model_atomic_mass(int Z, int A) const
Calculate a theoretical atomic mass using the liquid drop model.
Definition MassTable.cc:80
double get_binding_energy(int Z, int A, bool theory_ok=true) const
Get the binding energy of a nucleus.
Definition MassTable.cc:167
double get_particle_mass(int pdg_code) const
Get the mass of a particle.
Definition MassTable.cc:84
double liquid_drop_model_mass_excess(int Z, int A) const
Calculate a theoretical mass excess for a nucleus using the liquid drop model.
Definition MassTable.cc:238
double unbound_threshold(const int Zi, const int Ai) const
Computes the lowest excitation energy at which one of the nuclear fragments considered by the HauserF...
Definition MassTable.cc:281
MassTable()
Create the singleton MassTable object.
Definition MassTable.cc:30
static const std::map< int, marley::Fragment > & fragments()
Retrieves a const reference to the table of Fragment objects.