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::TabulatedXSec Class Reference

Computes inclusive lepton-nucleus cross sections using tabulated nuclear response functions. More...

#include <TabulatedXSec.hh>

Classes

struct  IntegralTerm
 Storage for terms in the integrals evaluated by compute_integral(). More...
 
struct  MultipoleLabel
 Simple struct representing a given multipole order, e.g., 2+. More...
 
struct  OptimizationMapKey
 
struct  OptimizationMapValue
 

Public Member Functions

 TabulatedXSec (int target_pdg, marley::Reaction::ProcessType p_type, marley::CoulombCorrector::CoulombMode mode, double delta_ias)
 
void add_table (const std::string &file_name)
 
void add_table (unsigned J, marley::Parity Pi, const std::string &file_name)
 
double compute_integral (int pdg_a, double KEa, const MultipoleLabel &ml, double &diff_max, std::vector< IntegralTerm > *integral_terms=nullptr) const
 Helper function for integral that does the actual integration.
 
double delta_ias () const
 Get the shift used to compute the effective energy transfer.
 
double diff_xsec (int pdg_a, double KE_a, double omega, double cos_theta, const MultipoleLabel &ml) const
 
const ResponseTableget_table (const MultipoleLabel &ml) const
 
const std::map< MultipoleLabel, ResponseTable > & get_table_map () const
 
double integral (int pdg_a, double KEa) const
 
double integral (int pdg_a, double KEa, const MultipoleLabel &ml, double &diff_max) const
 
bool is_cc () const
 Returns true if this cross section represents a CC process or false otherwise.
 
void optimize (int pdg_a, double max_KEa)
 
void unoptimize ()
 

Protected Attributes

marley::CoulombCorrector::CoulombMode coulomb_mode_
 Method to use for computing Coulomb corrections to the cross section.
 
double delta_ias_
 
std::map< OptimizationMapKey, OptimizationMapValueoptimization_map_
 
int pdg_d_
 
marley::Reaction::ProcessType proc_type_
 Kind of process for which the cross section will be computed.
 
std::map< MultipoleLabel, ResponseTableresponses_
 Tables of nuclear responses organized by multipole.
 
TargetAtom ta_
 Nuclide represented by this table of nuclear responses.
 

Detailed Description

Computes inclusive lepton-nucleus cross sections using tabulated nuclear response functions.

Definition at line 36 of file TabulatedXSec.hh.

Constructor & Destructor Documentation

◆ TabulatedXSec()

marley::TabulatedXSec::TabulatedXSec ( int target_pdg,
marley::Reaction::ProcessType p_type,
marley::CoulombCorrector::CoulombMode mode,
double delta_ias )
explicit

Definition at line 29 of file TabulatedXSec.cc.

31 : ta_( target_pdg ), proc_type_( p_type ),
33{
34 // Set the residue PDG code (pdg_d_) based on the input information
35 int dummy_charge;
37 pdg_d_, dummy_charge );
38}
static void get_residue_pdg_and_charge(ProcessType proc_type, int pdg_b, int &pdg_d, int &q_d)
Definition Reaction.cc:573
marley::Reaction::ProcessType proc_type_
Kind of process for which the cross section will be computed.
marley::CoulombCorrector::CoulombMode coulomb_mode_
Method to use for computing Coulomb corrections to the cross section.
TargetAtom ta_
Nuclide represented by this table of nuclear responses.
double delta_ias() const
Get the shift used to compute the effective energy transfer.

Member Function Documentation

◆ add_table()

void marley::TabulatedXSec::add_table ( const std::string & file_name)

Definition at line 40 of file TabulatedXSec.cc.

41{
42 const auto& fm = marley::FileManager::Instance();
43 std::string full_file_name = fm.find_file( file_name );
44 if ( full_file_name.empty() ) {
45 throw marley::Error( "Could not open the nuclear response data file \""
46 + file_name + '\"' );
47 }
48
49 std::ifstream in_file( full_file_name );
50
51 // TODO: add error handling for file parsing
52
53 // Multipole order
54 unsigned J;
55 in_file >> J;
56
57 // Grid sizes
58 unsigned num_w, num_q;
59
60 // Temporary storage for grid points
61 double w, q;
62
63 // 1D grids
64 auto wvec = std::make_shared< std::vector<double> >();
65 auto qvec = std::make_shared< std::vector<double> >();
66
67 in_file >> num_w;
68 for ( unsigned iw = 0u; iw < num_w; ++iw ) {
69 in_file >> w;
70 wvec->push_back( w );
71 }
72
73 in_file >> num_q;
74 for ( unsigned iq = 0u; iq < num_q; ++iq ) {
75 in_file >> q;
76 qvec->push_back( q );
77 }
78
79 // Natural parity is given by (-1)^J, while unnatural parity is the opposite
80 marley::Parity natural;
81 bool J_is_odd = (J % 2 == 1);
82 if ( J_is_odd ) natural = -1;
83 else natural = 1;
84
85 marley::Parity unnatural = -natural;
86
87 MultipoleLabel nat_ml( J, natural );
88 MultipoleLabel unnat_ml( J, unnatural );
89
90 auto nat_resp = std::make_shared<
91 std::vector<marley::NuclearResponses> >();
92
93 auto unnat_resp = std::make_shared<
94 std::vector<marley::NuclearResponses> >();
95
96 // Temporary storage for responses
97 double rcc, rll, rcl, rtVV, rtAA, rtprime;
98
99 // Flag used to swap back and forth between natural and unnatural parity
100 bool nat = true;
101 while ( in_file >> rcc >> rll >> rcl >> rtVV >> rtAA >> rtprime ) {
102
103 if ( nat ) nat_resp->emplace_back( rcc, rll, rcl, rtVV, rtAA, rtprime );
104 else unnat_resp->emplace_back( rcc, rll, rcl, rtVV, rtAA, rtprime );
105
106 // Flip the parity flag
107 nat = !nat;
108 }
109
110 // Store the finished tables as new objects in the map
111 responses_[ nat_ml ] = ResponseTable( wvec, qvec, nat_resp );
112 responses_[ unnat_ml ] = ResponseTable( wvec, qvec, unnat_resp );
113
114}
static const FileManager & Instance()
Get a const reference to the singleton instance of the FileManager.
std::map< MultipoleLabel, ResponseTable > responses_
Tables of nuclear responses organized by multipole.
Simple struct representing a given multipole order, e.g., 2+.

◆ compute_integral()

double marley::TabulatedXSec::compute_integral ( int pdg_a,
double KEa,
const MultipoleLabel & ml,
double & diff_max,
std::vector< IntegralTerm > * integral_terms = nullptr ) const

Helper function for integral that does the actual integration.

Parameters
[out]integral_termsOptional vector that will be cleared and filled with each term in the sum that leads to the value of the integral returned by the function. The terms are associated with energy transfer (omega, MeV) and scattering cosine (cos_theta, dimensionless) values in the IntegralTerm struct.

Definition at line 206 of file TabulatedXSec.cc.

209{
210 // Set the maximum differential cross section to zero to start
211 diff_max = 0.;
212
213 // If the input vector is not null, then clear it in preparation for
214 // accumulating the terms of the integral
215 if ( integral_terms ) integral_terms->clear();
216
217 // Get the table of nuclear responses for the requested multipole
218 const auto& rt = this->responses_.at( ml );
219
220 // Get the vectors of grid points
221 const auto& wvec = rt.w_grid();
222 const auto& qvec = rt.q_grid();
223
224 // TODO: reduce code duplication with this and diff_xsec
225 // Look up the masses of the projectile and ejectile
227 const auto& mt = marley::MassTable::Instance();
228 double ma = mt.get_particle_mass( pdg_a );
229 double mc = mt.get_particle_mass( pdg_c );
230
231 // Projectile total energy
232 double Ea = KEa + ma;
233
234 // Get the grid step sizes
235 // NOTE: this assumes that the grid is regularly spaced (I take advantage
236 // of this to simplify the trapezoidal rule for integration)
237 // TODO: revisit this assumption
238 size_t num_w = wvec.size();
239 size_t num_q = qvec.size();
240
241 size_t num_w_minus_one = num_w - 1;
242 size_t num_q_minus_one = num_q - 1;
243
244 double dw = ( wvec.back() - wvec.front() ) / num_w_minus_one;
245 double dq = ( qvec.back() - qvec.front() ) / num_q_minus_one;
246
247 // Loop over every grid point. Do an angular integral at every omega
248 // point, and use the results to integrate over omega.
249 double integ = 0.;
250 for ( size_t iw = 0u; iw < num_w; ++iw ) {
251
252 // Get the energy transfer at the current grid point. Note that the
253 // tables of nuclear responses are reported on a grid that uses the
254 // effective value (shifted by delta_ias_). We correct for this here.
255 double w_eff = wvec.at( iw );
256 double w = w_eff - this->delta_ias();
257
258 // Use it to compute the ejectile total energy, etc.
259 double Ec = Ea - w;
260 // We can skip unphysical terms for which the total energy is smaller than
261 // the final lepton mass
262 if ( Ec < mc ) continue;
263 double pc = marley_utils::real_sqrt( Ec*Ec - mc*mc );
264 double pa = marley_utils::real_sqrt( Ea*Ea - ma*ma );
265
266 double w_integ = 0.;
267 for ( size_t iq = 0u; iq < num_q; ++iq ) {
268
269 // Get the magnitude of the 3-momentum transfer at the current grid point
270 double q = qvec.at( iq );
271
272 // Convert it into a value for the scattering cosine
273 double cos_theta = ( pa*pa + pc*pc - q*q ) / ( 2.*pa*pc );
274
275 // Compute the differential cross section at this 2D grid point
276 double diff = this->diff_xsec( pdg_a, KEa, w, cos_theta, ml );
277
278 // If it is larger than any value encountered so far, record it as
279 // the new maximum
280 if ( diff > diff_max ) diff_max = diff;
281
282 // Apply a Jacobian to transform from dwdcostheta to dwdq (since we're
283 // integrating on a grid of q values)
284 diff *= q / pa / pc;
285
286 // Add this term to the integral over omega according to the trapezoid
287 // rule
288 if ( iq == 0u || iq == num_q_minus_one ) diff /= 2.;
289 w_integ += diff;
290
291 // If the user supplied a vector to record the terms in the integral,
292 // then store the contribution from the current iteration before
293 // continuing to the next grid point
294 if ( integral_terms ) {
295 double value = diff * dq * dw;
296 if ( iw == 0u || iw == num_w_minus_one ) value /= 2.;
297 integral_terms->emplace_back( w, cos_theta, value );
298 }
299
300 } // q grid points
301
302 // We're done summing over q values. Scale the result to get the
303 // integral over q for this w grid point.
304 w_integ *= dq;
305
306 // Now add this term to the integral over w according to the trapezoid rule
307 if ( iw == 0u || iw == num_w_minus_one ) w_integ /= 2.;
308
309 integ += w_integ;
310
311 } // w grid points
312
313 // We're done. Scale the integral over w by the needed prefactor
314 integ *= dw;
315
316 return integ;
317}
static const MassTable & Instance()
Get a const reference to the singleton instance of the MassTable.
Definition MassTable.cc:69
static int get_ejectile_pdg(int pdg_a, ProcessType proc_type)
Definition Reaction.cc:329

References delta_ias(), marley::Reaction::get_ejectile_pdg(), marley::MassTable::Instance(), proc_type_, and responses_.

◆ delta_ias()

double marley::TabulatedXSec::delta_ias ( ) const

Get the shift used to compute the effective energy transfer.

Definition at line 448 of file TabulatedXSec.cc.

448 {
449 bool is_charged_current = this->is_cc();
450
451 double result = 0.;
452 if ( is_charged_current ) result = delta_ias_;
453 return result;
454}
bool is_cc() const
Returns true if this cross section represents a CC process or false otherwise.

References delta_ias_, and is_cc().

Referenced by compute_integral().

◆ diff_xsec()

double marley::TabulatedXSec::diff_xsec ( int pdg_a,
double KE_a,
double omega,
double cos_theta,
const MultipoleLabel & ml ) const

Definition at line 116 of file TabulatedXSec.cc.

118{
119 int helicity = marley_utils::get_particle_helicity( pdg_a );
120
121 // Look up the masses of the projectile and ejectile
123 const auto& mt = marley::MassTable::Instance();
124 double ma = mt.get_particle_mass( pdg_a );
125 double mc = mt.get_particle_mass( pdg_c );
126
127 // Determine the total energies and momenta of the initial and final leptons
128 double Ea = KEa + ma;
129 double pa = marley_utils::real_sqrt( Ea*Ea - ma*ma );
130 double Ec = Ea - omega;
131 if ( Ec < mc ) return 0.;
132
133 double pc = marley_utils::real_sqrt( Ec*Ec - mc*mc );
134
135 // Get the magnitude of the 3-momentum transfer (q) from this information
136 // and the scattering cosine. If the scattering cosine is unphysical, then
137 // just return zero.
138 if ( std::abs(cos_theta) > 1. ) return 0.;
139 double q = marley_utils::real_sqrt( pa*pa + pc*pc - 2.*pa*pc*cos_theta );
140
141 // Get the table of pre-computed nuclear responses for the requested
142 // multipole
143 const auto& rt = this->responses_.at( ml );
144
145 // Shift the energy transfer at which the responses are evaluated. This
146 // effective value of the energy transfer tries to correct for using the
147 // same nuclear potential for the initial and final states despite
148 // a change of nuclear charge for charged-current interactions.
149 double omega_eff = omega + this->delta_ias();
150
151 // Interpolate a set of nuclear responses for the given omega_eff and q values
152 if ( omega_eff < rt.w_min() || omega_eff > rt.w_max()
153 || q < rt.q_min() || q > rt.q_max() ) return 0.;
154 auto nr = rt.interpolate( omega_eff, q );
155
156 // Compute the lepton factors
157 double beta = pc / Ec;
158 double sin_theta2 = 1. - cos_theta*cos_theta;
159 double q2 = q*q;
160 double vcc = 1. + beta * cos_theta;
161 double vll = vcc - 2.*Ea*Ec*sin_theta2*beta*beta/q2;
162 double vcl = -2. * ( omega*vcc/q + mc*mc/Ec/q );
163 double vT = 1. - beta*cos_theta + Ea*Ec*beta*beta*sin_theta2/q2;
164 double vTprime = 2. * helicity * ( (Ea + Ec)*(1 - beta*cos_theta)/q
165 - mc*mc/q/Ec );
166 LeptonFactors lf( vcc, vll, vcl, vT, vTprime );
167
168 // Compute the double-differential cross section with respect to
169 // the energy transfer and the scattering cosine
170 double xsec = lf * nr;
171 xsec *= 2. * marley_utils::GF2 * marley_utils::Vud2 * Ec * pc;
172
173 // For CC cross sections, apply the appropriate correction factor for
174 // Coulomb corrections based on the active "Coulomb mode"
175 bool is_charged_current = this->is_cc();
176
177 if ( is_charged_current ) {
178
179 // Get the mass of the atomic target
180 double mb = mt.get_atomic_mass( ta_.pdg() );
181
182 // Lab-frame total energy of the residue (via energy conservation)
183 double Ed = omega + mb;
184
185 // Mass of the residue (via 3-momentum conservation)
186 double md = marley_utils::real_sqrt( Ed*Ed - q*q );
187
188 // Dot product of the 4-momenta of the ejectile and residue
189 double pc_dot_pd = Ec*Ed + pc*pc - pa*pc*cos_theta;
190
191 // Manifestly Lorentz-invariant relative speed of particles c and d
192 double beta_rel_cd = marley_utils::real_sqrt(
193 std::pow(pc_dot_pd, 2) - mc*mc*md*md ) / pc_dot_pd;
194
195 // Compute the Coulomb correction factor using the relative speed
196 CoulombCorrector coul_corr( pdg_c, pdg_d_, coulomb_mode_ );
197 double FC = coul_corr.coulomb_correction_factor( beta_rel_cd );
198
199 // Apply it to the differential cross section
200 xsec *= FC;
201 }
202
203 return xsec;
204}

◆ get_table()

const ResponseTable & marley::TabulatedXSec::get_table ( const MultipoleLabel & ml) const
inline

Definition at line 83 of file TabulatedXSec.hh.

84 { return responses_.at( ml ); }

◆ get_table_map()

const std::map< MultipoleLabel, ResponseTable > & marley::TabulatedXSec::get_table_map ( ) const
inline

Definition at line 86 of file TabulatedXSec.hh.

87 { return responses_; }

◆ integral() [1/2]

double marley::TabulatedXSec::integral ( int pdg_a,
double KEa ) const

Definition at line 354 of file TabulatedXSec.cc.

355{
356 double integ = 0.;
357 for ( const auto& pair : responses_ ) {
358 double dummy;
359 const auto& ml = pair.first;
360 double integ_ml = this->integral( pdg_a, KEa, ml, dummy );
361 integ += integ_ml;
362 }
363 return integ;
364}

◆ integral() [2/2]

double marley::TabulatedXSec::integral ( int pdg_a,
double KEa,
const MultipoleLabel & ml,
double & diff_max ) const

Definition at line 319 of file TabulatedXSec.cc.

321{
322 // First attempt to look up a pair of ChebyshevInterpolatingFunction
323 // objects for the given projectile PDG code and multipole
324 OptimizationMapKey key( pdg_a, ml );
325 auto end = optimization_map_.end();
326 auto iter = optimization_map_.find( key );
327 // If we found one, attempt to compute the cross sections using them
328 if ( iter != end ) {
329 const auto& omv = iter->second;
330 const auto& tot = omv.tot_xsec_;
331 // If the requested kinetic energy is below threshold, then just
332 // return zero
333 if ( KEa < tot.x_min() ) return 0.;
334 // If the kinetic energy is within the range covered by the
335 // interpolating functions, then go ahead and use them
336 else if ( KEa <= tot.x_max() ) {
337 double tot_xsec = omv.tot_xsec_.evaluate( KEa );
338 diff_max = omv.max_diff_xsec_.evaluate( KEa );
339
340 // Ensure that interpolation problems will not give us a negative value
341 // for the total or maximum differential cross sections
342 tot_xsec = std::max( 0., tot_xsec );
343 diff_max = std::max( 0., diff_max );
344
345 return tot_xsec;
346 }
347 }
348
349 // If we couldn't find a suitable interpolating function, then fall back to
350 // brute-force integration
351 return this->compute_integral( pdg_a, KEa, ml, diff_max );
352}
std::map< OptimizationMapKey, OptimizationMapValue > optimization_map_
double compute_integral(int pdg_a, double KEa, const MultipoleLabel &ml, double &diff_max, std::vector< IntegralTerm > *integral_terms=nullptr) const
Helper function for integral that does the actual integration.

◆ is_cc()

bool marley::TabulatedXSec::is_cc ( ) const
inline

Returns true if this cross section represents a CC process or false otherwise.

Definition at line 51 of file TabulatedXSec.hh.

51 {
52 bool is_cc = ( proc_type_ == ProcType::NeutrinoCC_Discrete
53 || proc_type_ == ProcType::AntiNeutrinoCC_Discrete
54 || proc_type_ == ProcType::NeutrinoCC_Continuum
55 || proc_type_ == ProcType::AntiNeutrinoCC_Continuum );
56 return is_cc;
57 }

References is_cc(), and proc_type_.

Referenced by delta_ias(), and is_cc().

◆ optimize()

void marley::TabulatedXSec::optimize ( int pdg_a,
double max_KEa )

Definition at line 366 of file TabulatedXSec.cc.

366 {
367 MARLEY_LOG( INFO, "physics.reaction.xsec" ) << "Optimizing CRPA cross"
368 " section up to " << max_KEa << " MeV";
369 // Loop over each of the multipoles
370 for ( const auto& pair : responses_ ) {
371 const auto& ml = pair.first;
372
373 double min_KEa = 0.;
374 std::function<double(double)> tot_xsec_func = [](double)
375 -> double { return 0.; };
376 std::function<double(double)> max_diff_xsec_func = tot_xsec_func;
377
378 // Verify that the total cross section for this multipole is non-vanishing
379 // for the requested maximum projectile kinetic energy KEa. If it vanishes,
380 // then just skip the current multipole.
381 double dummy;
382 double xsec_at_max = this->integral( pdg_a, max_KEa, ml, dummy );
383
384 if ( xsec_at_max > 0. ) {
385 // Do a binary search to find the threshold for this multipole. Continue
386 // until we've found it within the given tolerance.
387 constexpr double thresh_tol = 1e-6; // MeV
388 // Set up the bounds of a bracketing interval that will contain the
389 // kinetic energy threshold
390 double low_KEa = 0.;
391 double high_KEa = max_KEa;
392 do {
393 // Check the total cross section at the midpoint of the current
394 // bracketing interval
395 double cur_KEa = (low_KEa + high_KEa) / 2.;
396 double xsec = this->integral( pdg_a, cur_KEa, ml, dummy );
397 // If it vanishes, move the lower bound up
398 if ( xsec <= 0. ) low_KEa = cur_KEa;
399 // If it doesn't, move the upper bound down
400 else high_KEa = cur_KEa;
401 // Continue until the bracketing interval is no larger than the
402 // tolerance defined above
403 } while ( std::abs(high_KEa - low_KEa) > thresh_tol );
404
405 // Adopt the lower bound of the bracketing interval as the threshold
406 min_KEa = low_KEa;
407
408 tot_xsec_func = [&, this](double KEa)
409 -> double { return this->integral( pdg_a, KEa, ml, dummy ); };
410
411 max_diff_xsec_func = [&, this](double KEa) -> double {
412 double max_diff;
413 this->integral( pdg_a, KEa, ml, max_diff );
414 return max_diff;
415 };
416 }
417
418 MARLEY_LOG( DEBUG, "physics.reaction.xsec" ) << "Optimizing total cross section for "
419 << ml.J_ << ml.Pi_ << " over KE in ["
420 << min_KEa << ", " << max_KEa << "] MeV";
421
422 // Now we're ready to build the Chebyshev interpolating functions for
423 // this multipole. We need one for the total cross section and the
424 // other one for the maximum of the differential cross section.
425 ChebyshevInterpolatingFunction tot_xs_cif( tot_xsec_func, min_KEa,
426 max_KEa, 64 );
427 // TODO: do you want adaptive grid sizing here?
428
429 MARLEY_LOG( DEBUG, "physics.reaction.xsec" ) << "Optimizing max diff for "
430 << ml.J_ << ml.Pi_;
431
432 ChebyshevInterpolatingFunction max_diff_xs_cif( max_diff_xsec_func,
433 min_KEa, max_KEa, 64 );
434
435 // Build the key and value we need for the map entry
436 OptimizationMapKey key( pdg_a, ml );
437 OptimizationMapValue value( tot_xs_cif, max_diff_xs_cif );
438
439 optimization_map_.emplace( std::make_pair(key, value) );
440 }
441}

◆ unoptimize()

void marley::TabulatedXSec::unoptimize ( )
inline

Definition at line 96 of file TabulatedXSec.hh.

96{ optimization_map_.clear(); }

Member Data Documentation

◆ coulomb_mode_

marley::CoulombCorrector::CoulombMode marley::TabulatedXSec::coulomb_mode_
protected

Method to use for computing Coulomb corrections to the cross section.

Definition at line 158 of file TabulatedXSec.hh.

◆ delta_ias_

double marley::TabulatedXSec::delta_ias_
protected

Effective shift in the energy transfer that accounts for the mass difference between the ground state of the initial nucleus and the isobaric analog state in the daughter nucleus

Definition at line 174 of file TabulatedXSec.hh.

Referenced by delta_ias().

◆ optimization_map_

std::map< OptimizationMapKey, OptimizationMapValue > marley::TabulatedXSec::optimization_map_
protected

Storage for interpolating functions used to optimize calculations requiring numerical integration

Definition at line 169 of file TabulatedXSec.hh.

◆ pdg_d_

int marley::TabulatedXSec::pdg_d_
protected

PDG code of the nuclear residue produced by the reaction described by this cross section

Definition at line 162 of file TabulatedXSec.hh.

◆ proc_type_

marley::Reaction::ProcessType marley::TabulatedXSec::proc_type_
protected

Kind of process for which the cross section will be computed.

Definition at line 155 of file TabulatedXSec.hh.

Referenced by compute_integral(), and is_cc().

◆ responses_

std::map< MultipoleLabel, ResponseTable > marley::TabulatedXSec::responses_
protected

Tables of nuclear responses organized by multipole.

Definition at line 165 of file TabulatedXSec.hh.

Referenced by compute_integral().

◆ ta_

TargetAtom marley::TabulatedXSec::ta_
protected

Nuclide represented by this table of nuclear responses.

Definition at line 152 of file TabulatedXSec.hh.


The documentation for this class was generated from the following files: