ARTEMIS
SingleNuclearFusionEvent.H
Go to the documentation of this file.
1 /* Copyright 2021 Neil Zaim
2  *
3  * This file is part of WarpX.
4  *
5  * License: BSD-3-Clause-LBNL
6  */
7 
8 #ifndef SINGLE_NUCLEAR_FUSION_EVENT_H_
9 #define SINGLE_NUCLEAR_FUSION_EVENT_H_
10 
13 
15 #include "Utils/WarpXConst.H"
16 
17 #include <AMReX_Algorithm.H>
18 #include <AMReX_Math.H>
19 #include <AMReX_Random.H>
20 #include <AMReX_REAL.H>
21 
22 #include <cmath>
23 
24 
53 template <typename index_type>
55 void SingleNuclearFusionEvent (const amrex::ParticleReal& u1x, const amrex::ParticleReal& u1y,
56  const amrex::ParticleReal& u1z, const amrex::ParticleReal& u2x,
57  const amrex::ParticleReal& u2y, const amrex::ParticleReal& u2z,
58  const amrex::ParticleReal& m1, const amrex::ParticleReal& m2,
59  amrex::ParticleReal w1, amrex::ParticleReal w2,
60  const amrex::Real& dt, const amrex::ParticleReal& dV, const int& pair_index,
61  index_type* AMREX_RESTRICT p_mask,
62  amrex::ParticleReal* AMREX_RESTRICT p_pair_reaction_weight,
63  const amrex::ParticleReal& fusion_multiplier,
64  const int& multiplier_ratio,
65  const amrex::ParticleReal& probability_threshold,
66  const amrex::ParticleReal& probability_target_value,
67  const NuclearFusionType& fusion_type,
68  const amrex::RandomEngine& engine)
69 {
70  // General notations in this function:
71  // x_sq denotes the square of x
72  // x_star denotes the value of x in the center of mass frame
73 
74  using namespace amrex::literals;
75  using namespace amrex::Math;
76 
77  const amrex::ParticleReal w_min = amrex::min(w1, w2);
78  const amrex::ParticleReal w_max = amrex::max(w1, w2);
79 
80  constexpr auto one_pr = amrex::ParticleReal(1.);
81  constexpr auto inv_four_pr = amrex::ParticleReal(1./4.);
82  constexpr amrex::ParticleReal c_sq = PhysConst::c * PhysConst::c;
83  constexpr amrex::ParticleReal inv_csq = one_pr / ( c_sq );
84 
85  const amrex::ParticleReal m1_sq = m1*m1;
86  const amrex::ParticleReal m2_sq = m2*m2;
87 
88  // Compute Lorentz factor gamma in the lab frame
89  const amrex::ParticleReal g1 = std::sqrt( one_pr + (u1x*u1x+u1y*u1y+u1z*u1z)*inv_csq );
90  const amrex::ParticleReal g2 = std::sqrt( one_pr + (u2x*u2x+u2y*u2y+u2z*u2z)*inv_csq );
91 
92  // Compute momenta
93  const amrex::ParticleReal p1x = u1x * m1;
94  const amrex::ParticleReal p1y = u1y * m1;
95  const amrex::ParticleReal p1z = u1z * m1;
96  const amrex::ParticleReal p2x = u2x * m2;
97  const amrex::ParticleReal p2y = u2y * m2;
98  const amrex::ParticleReal p2z = u2z * m2;
99  // Square norm of the total (sum between the two particles) momenta in the lab frame
100  const amrex::ParticleReal p_total_sq = powi<2>(p1x + p2x) +
101  powi<2>(p1y+p2y) +
102  powi<2>(p1z+p2z);
103 
104  // Total energy in the lab frame
105  const amrex::ParticleReal E_lab = (m1 * g1 + m2 * g2) * c_sq;
106  // Total energy squared in the center of mass frame, calculated using the Lorentz invariance
107  // of the four-momentum norm
108  const amrex::ParticleReal E_star_sq = E_lab*E_lab - c_sq*p_total_sq;
109 
110  // Kinetic energy in the center of mass frame
111  const amrex::ParticleReal E_star = std::sqrt(E_star_sq);
112  const amrex::ParticleReal E_kin_star = E_star - (m1 + m2)*c_sq;
113 
114  // Compute fusion cross section as a function of kinetic energy in the center of mass frame
115  auto fusion_cross_section = amrex::ParticleReal(0.);
116  if (fusion_type == NuclearFusionType::ProtonBoronToAlphas)
117  {
118  fusion_cross_section = ProtonBoronFusionCrossSection(E_kin_star);
119  }
123  {
124  fusion_cross_section = BoschHaleFusionCrossSection(E_kin_star, fusion_type, m1, m2);
125  }
126 
127  // Square of the norm of the momentum of one of the particles in the center of mass frame
128  // Formula obtained by inverting E^2 = p^2*c^2 + m^2*c^4 in the COM frame for each particle
129  // The expression below is specifically written in a form that avoids returning
130  // small negative numbers due to machine precision errors, for low-energy particles
131  const amrex::ParticleReal E_ratio = E_star/((m1 + m2)*c_sq);
132  const amrex::ParticleReal p_star_sq = m1*m2*c_sq * ( powi<2>(E_ratio) - one_pr )
133  + powi<2>(m1 - m2)*c_sq*inv_four_pr * powi<2>( E_ratio - 1._prt/E_ratio );
134 
135  // Lorentz factors in the center of mass frame
136  const amrex::ParticleReal g1_star = std::sqrt(one_pr + p_star_sq / (m1_sq*c_sq));
137  const amrex::ParticleReal g2_star = std::sqrt(one_pr + p_star_sq / (m2_sq*c_sq));
138 
139  // relative velocity in the center of mass frame
140  const amrex::ParticleReal v_rel = std::sqrt(p_star_sq) * (one_pr/(m1*g1_star) +
141  one_pr/(m2*g2_star));
142 
143  // Fusion cross section and relative velocity are computed in the center of mass frame.
144  // On the other hand, the particle densities (weight over volume) in the lab frame are used. To
145  // take into account this discrepancy, we need to multiply the fusion probability by the ratio
146  // between the Lorentz factors in the COM frame and the Lorentz factors in the lab frame
147  // (see Perez et al., Phys.Plasmas.19.083104 (2012))
148  const amrex::ParticleReal lab_to_COM_factor = g1_star*g2_star/(g1*g2);
149 
150  // First estimate of probability to have fusion reaction
151  amrex::ParticleReal probability_estimate = multiplier_ratio * fusion_multiplier *
152  lab_to_COM_factor * w_max * fusion_cross_section * v_rel * dt / dV;
153 
154  // Effective fusion multiplier
155  amrex::ParticleReal fusion_multiplier_eff = fusion_multiplier;
156 
157  // If the fusion probability is too high and the fusion multiplier greater than one, we risk to
158  // systematically underestimate the fusion yield. In this case, we reduce the fusion multiplier
159  // to reduce the fusion probability
160  if (probability_estimate > probability_threshold)
161  {
162  // We aim for a fusion probability of probability_target_value but take into account
163  // the constraint that the fusion_multiplier cannot be smaller than one
164  fusion_multiplier_eff = amrex::max(fusion_multiplier *
165  probability_target_value / probability_estimate , one_pr);
166  probability_estimate *= fusion_multiplier_eff/fusion_multiplier;
167  }
168 
169  // Compute actual fusion probability that is always between zero and one
170  // In principle this is obtained by computing 1 - exp(-probability_estimate)
171  // However, the computation of this quantity can fail numerically when probability_estimate is
172  // too small (e.g. exp(-probability_estimate) returns 1 and the computation returns 0).
173  // In this case, we simply use "probability_estimate" instead of 1 - exp(-probability_estimate)
174  // The threshold exp_threshold at which we switch between the two formulas is determined by the
175  // fact that computing the exponential is only useful if it can resolve the x^2/2 term of its
176  // Taylor expansion, i.e. the square of probability_estimate should be greater than the
177  // machine epsilon.
178 #ifdef AMREX_SINGLE_PRECISION_PARTICLES
179  constexpr auto exp_threshold = amrex::ParticleReal(1.e-3);
180 #else
181  constexpr auto exp_threshold = amrex::ParticleReal(5.e-8);
182 #endif
183  const amrex::ParticleReal probability = (probability_estimate < exp_threshold) ?
184  probability_estimate: one_pr - std::exp(-probability_estimate);
185 
186  // Get a random number
187  amrex::ParticleReal random_number = amrex::Random(engine);
188 
189  // If we have a fusion event, set the mask the true and fill the product weight array
190  if (random_number < probability)
191  {
192  p_mask[pair_index] = true;
193  p_pair_reaction_weight[pair_index] = w_min/fusion_multiplier_eff;
194  }
195  else
196  {
197  p_mask[pair_index] = false;
198  }
199 
200 }
201 
202 
203 #endif // SINGLE_NUCLEAR_FUSION_EVENT_H_
#define AMREX_INLINE
#define AMREX_GPU_HOST_DEVICE
NuclearFusionType
Definition: BinaryCollisionUtils.H:22
@ DeuteriumDeuteriumToProtonTritium
@ DeuteriumDeuteriumToNeutronHelium
AMREX_GPU_HOST_DEVICE AMREX_INLINE amrex::ParticleReal BoschHaleFusionCrossSection(const amrex::ParticleReal &E_kin_star, const NuclearFusionType &fusion_type, const amrex::ParticleReal &m1, const amrex::ParticleReal &m2)
Computes the fusion cross section, using the analytical fits given in H.-S. Bosch and G....
Definition: BoschHaleFusionCrossSection.H:29
AMREX_GPU_HOST_DEVICE AMREX_INLINE amrex::ParticleReal ProtonBoronFusionCrossSection(const amrex::ParticleReal &E_kin_star)
Computes the total proton-boron fusion cross section. When E_kin_star < 3.5 MeV, we use the analytica...
Definition: ProtonBoronFusionCrossSection.H:138
AMREX_GPU_HOST_DEVICE AMREX_INLINE void SingleNuclearFusionEvent(const amrex::ParticleReal &u1x, const amrex::ParticleReal &u1y, const amrex::ParticleReal &u1z, const amrex::ParticleReal &u2x, const amrex::ParticleReal &u2y, const amrex::ParticleReal &u2z, const amrex::ParticleReal &m1, const amrex::ParticleReal &m2, amrex::ParticleReal w1, amrex::ParticleReal w2, const amrex::Real &dt, const amrex::ParticleReal &dV, const int &pair_index, index_type *AMREX_RESTRICT p_mask, amrex::ParticleReal *AMREX_RESTRICT p_pair_reaction_weight, const amrex::ParticleReal &fusion_multiplier, const int &multiplier_ratio, const amrex::ParticleReal &probability_threshold, const amrex::ParticleReal &probability_target_value, const NuclearFusionType &fusion_type, const amrex::RandomEngine &engine)
This function computes whether the collision between two particles result in a nuclear fusion event,...
Definition: SingleNuclearFusionEvent.H:55
ParticleBins::index_type index_type
Definition: ParticleUtils.cpp:32
static constexpr auto c
vacuum speed of light [m/s]
Definition: constant.H:44
Real Random()
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE constexpr const T & min(const T &a, const T &b) noexcept
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE constexpr const T & max(const T &a, const T &b) noexcept
int dt
Definition: stencil.py:440