aboutsummaryrefslogtreecommitdiff
path: root/Source/Particles/Collision/BinaryCollision/NuclearFusion/SingleNuclearFusionEvent.H
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Particles/Collision/BinaryCollision/NuclearFusion/SingleNuclearFusionEvent.H')
-rw-r--r--Source/Particles/Collision/BinaryCollision/NuclearFusion/SingleNuclearFusionEvent.H190
1 files changed, 190 insertions, 0 deletions
diff --git a/Source/Particles/Collision/BinaryCollision/NuclearFusion/SingleNuclearFusionEvent.H b/Source/Particles/Collision/BinaryCollision/NuclearFusion/SingleNuclearFusionEvent.H
new file mode 100644
index 000000000..c8633b52b
--- /dev/null
+++ b/Source/Particles/Collision/BinaryCollision/NuclearFusion/SingleNuclearFusionEvent.H
@@ -0,0 +1,190 @@
+/* Copyright 2021 Neil Zaim
+ *
+ * This file is part of WarpX.
+ *
+ * License: BSD-3-Clause-LBNL
+ */
+
+#ifndef SINGLE_NUCLEAR_FUSION_EVENT_H_
+#define SINGLE_NUCLEAR_FUSION_EVENT_H_
+
+#include "ProtonBoronFusionCrossSection.H"
+
+#include "Particles/Collision/BinaryCollision/BinaryCollisionUtils.H"
+#include "Utils/WarpXConst.H"
+
+#include <AMReX_Algorithm.H>
+#include <AMReX_Random.H>
+#include <AMReX_REAL.H>
+
+#include <cmath>
+
+
+/**
+ * \brief This function computes whether the collision between two particles result in a
+ * nuclear fusion event, using the algorithm described in Higginson et al., Journal of
+ * Computational Physics 388, 439-453 (2019). If nuclear fusion occurs, the mask is set to true
+ * for that given pair of particles and the weight of the produced particles is stored in
+ * p_pair_reaction_weight.
+ *
+ * @tparam index_type type of the index argument
+ * @param[in] u1x,u1y,u1z momenta of the first colliding particle
+ * @param[in] u2x,u2y,u2z momenta of the second colliding particle
+ * @param[in] m1,m2 masses
+ * @param[in] w1,w2 effective weight of the colliding particles
+ * @param[in] dt is the time step length between two collision calls.
+ * @param[in] dV is the volume of the corresponding cell.
+ * @param[in] pair_index is the index of the colliding pair
+ * @param[out] p_mask is a mask that will be set to true if fusion occurs for that pair
+ * @param[out] p_pair_reaction_weight stores the weight of the product particles
+ * @param[in] fusion_multiplier factor used to increase the number of fusion events by
+ * decreasing the weight of the produced particles
+ * @param[in] multiplier_ratio factor used to take into account unsampled pairs (i.e. the fact
+ * that a particle only collides with one or few particles of the other species)
+ * @param[in] probability_threshold probability threshold above which we decrease the fusion
+ * multiplier
+ * @param[in] probability_target_value if the probability threshold is exceeded, this is used
+ * to determine by how much the fusion multiplier is reduced
+ * @param[in] fusion_type the physical fusion process to model
+ * @param[in] engine the random engine.
+ */
+template <typename index_type>
+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::Real& dV, const int& pair_index,
+ index_type* AMREX_RESTRICT p_mask,
+ amrex::ParticleReal* AMREX_RESTRICT p_pair_reaction_weight,
+ const amrex::Real& fusion_multiplier,
+ const int& multiplier_ratio,
+ const amrex::Real& probability_threshold,
+ const amrex::Real& probability_target_value,
+ const NuclearFusionType& fusion_type,
+ const amrex::RandomEngine& engine)
+{
+ // General notations in this function:
+ // x_sq denotes the square of x
+ // x_star denotes the value of x in the center of mass frame
+
+ const amrex::ParticleReal w_min = amrex::min(w1, w2);
+ const amrex::ParticleReal w_max = amrex::max(w1, w2);
+
+ constexpr auto one_pr = amrex::ParticleReal(1.);
+ constexpr auto inv_two_pr = amrex::ParticleReal(1./2.);
+ constexpr auto inv_four_pr = amrex::ParticleReal(1./4.);
+ constexpr amrex::ParticleReal c_sq = PhysConst::c * PhysConst::c;
+ constexpr amrex::ParticleReal inv_csq = one_pr / ( c_sq );
+
+ const amrex::ParticleReal m1_sq = m1*m1;
+ const amrex::ParticleReal m2_sq = m2*m2;
+
+ // Compute Lorentz factor gamma in the lab frame
+ const amrex::ParticleReal g1 = std::sqrt( one_pr + (u1x*u1x+u1y*u1y+u1z*u1z)*inv_csq );
+ const amrex::ParticleReal g2 = std::sqrt( one_pr + (u2x*u2x+u2y*u2y+u2z*u2z)*inv_csq );
+
+ // Compute momenta
+ const amrex::ParticleReal p1x = u1x * m1;
+ const amrex::ParticleReal p1y = u1y * m1;
+ const amrex::ParticleReal p1z = u1z * m1;
+ const amrex::ParticleReal p2x = u2x * m2;
+ const amrex::ParticleReal p2y = u2y * m2;
+ const amrex::ParticleReal p2z = u2z * m2;
+ // Square norm of the total (sum between the two particles) momenta in the lab frame
+ const amrex::ParticleReal p_total_sq = std::pow(p1x+p2x, 2) +
+ std::pow(p1y+p2y, 2) +
+ std::pow(p1z+p2z, 2);
+
+ // Total energy in the lab frame
+ const amrex::ParticleReal E_lab = (m1 * g1 + m2 * g2) * c_sq;
+ // Total energy squared in the center of mass frame, calculated using the Lorentz invariance
+ // of the four-momentum norm
+ const amrex::ParticleReal E_star_sq = E_lab*E_lab - c_sq*p_total_sq;
+
+ // Kinetic energy in the center of mass frame
+ const amrex::ParticleReal E_kin_star = std::sqrt(E_star_sq) - (m1 + m2)*c_sq;
+
+ // Compute fusion cross section as a function of kinetic energy in the center of mass frame
+ auto fusion_cross_section = amrex::ParticleReal(0.);
+ if (fusion_type == NuclearFusionType::ProtonBoron)
+ {
+ fusion_cross_section = ProtonBoronFusionCrossSection(E_kin_star);
+ }
+
+ // Square of the norm of the momentum of one of the particles in the center of mass frame
+ // Formula obtained by inverting E^2 = p^2*c^2 + m^2*c^4 in the COM frame for each particle
+ const amrex::ParticleReal p_star_sq =
+ E_star_sq*inv_four_pr*inv_csq - (m1_sq + m2_sq)*c_sq*inv_two_pr +
+ std::pow(c_sq, 3)*inv_four_pr*std::pow(m1_sq - m2_sq, 2)/E_star_sq;
+
+ // Lorentz factors in the center of mass frame
+ const amrex::ParticleReal g1_star = std::sqrt(1 + p_star_sq / (m1_sq*c_sq));
+ const amrex::ParticleReal g2_star = std::sqrt(1 + p_star_sq / (m2_sq*c_sq));
+
+ // relative velocity in the center of mass frame
+ const amrex::ParticleReal v_rel = std::sqrt(p_star_sq) * (one_pr/(m1*g1_star) +
+ one_pr/(m2*g2_star));
+
+ // Fusion cross section and relative velocity are computed in the center of mass frame.
+ // On the other hand, the particle densities (weight over volume) in the lab frame are used. To
+ // take into account this discrepancy, we need to multiply the fusion probability by the ratio
+ // between the Lorentz factors in the COM frame and the Lorentz factors in the lab frame
+ // (see Perez et al., Phys.Plasmas.19.083104 (2012))
+ const amrex::ParticleReal lab_to_COM_factor = g1_star*g2_star/(g1*g2);
+
+ // First estimate of probability to have fusion reaction
+ amrex::ParticleReal probability_estimate = multiplier_ratio * fusion_multiplier *
+ lab_to_COM_factor * w_max * fusion_cross_section * v_rel * dt / dV;
+
+ // Effective fusion multiplier
+ amrex::ParticleReal fusion_multiplier_eff = fusion_multiplier;
+
+ // If the fusion probability is too high and the fusion multiplier greater than one, we risk to
+ // systematically underestimate the fusion yield. In this case, we reduce the fusion multiplier
+ // to reduce the fusion probability
+ if (probability_estimate > probability_threshold)
+ {
+ // We aim for a fusion probability of probability_target_value but take into account
+ // the constraint that the fusion_multiplier cannot be smaller than one
+ fusion_multiplier_eff = amrex::max(fusion_multiplier *
+ probability_target_value / probability_estimate , one_pr);
+ probability_estimate *= fusion_multiplier_eff/fusion_multiplier;
+ }
+
+ // Compute actual fusion probability that is always between zero and one
+ // In principle this is obtained by computing 1 - exp(-probability_estimate)
+ // However, the computation of this quantity can fail numerically when probability_estimate is
+ // too small (e.g. exp(-probability_estimate) returns 1 and the computation returns 0).
+ // In this case, we simply use "probability_estimate" instead of 1 - exp(-probability_estimate)
+ // The threshold exp_threshold at which we switch between the two formulas is determined by the
+ // fact that computing the exponential is only useful if it can resolve the x^2/2 term of its
+ // Taylor expansion, i.e. the square of probability_estimate should be greater than the
+ // machine epsilon.
+#ifdef AMREX_SINGLE_PRECISION_PARTICLES
+ constexpr auto exp_threshold = amrex::ParticleReal(1.e-3);
+#else
+ constexpr auto exp_threshold = amrex::ParticleReal(5.e-8);
+#endif
+ const amrex::ParticleReal probability = (probability_estimate < exp_threshold) ?
+ probability_estimate: one_pr - std::exp(-probability_estimate);
+
+ // Get a random number
+ amrex::ParticleReal random_number = amrex::Random(engine);
+
+ // If we have a fusion event, set the mask the true and fill the product weight array
+ if (random_number < probability)
+ {
+ p_mask[pair_index] = true;
+ p_pair_reaction_weight[pair_index] = w_min/fusion_multiplier_eff;
+ }
+ else
+ {
+ p_mask[pair_index] = false;
+ }
+
+}
+
+
+#endif // SINGLE_NUCLEAR_FUSION_EVENT_H_