Skip to content

Commit

Permalink
Binary-pairing Coulomb collisions: improvements and optimization (#5047)
Browse files Browse the repository at this point in the history
* Refactored binary collision method. Density and temperature are only computed once per cell, if need, rather than at each binary pairing (order Npcc^2 cost). Coulomb method now produces correct scattering physics with weighted particles.

* updating checksum files.

* fixing type issues.

* creating CI test for weighted coulomb scattering.

* fixing typos in comments.

* fixed recent merge issue.

* updating CI input/analysis

* updating comment.

* fixed 1d input

* < ==> <=

* updating reference in comment.

* adding comment for s12 expression.

* collisionsXZ.json flip ele ion

* updating checksums

* fixing merge issue with checksums.

* updating collisionXZ checksum.

* updating checksum.

* updating comments.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* adding paper section ref. to comment.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
JustinRayAngus and pre-commit-ci[bot] authored Aug 23, 2024
1 parent 1b342a3 commit e56ce8b
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 72 deletions.
12 changes: 6 additions & 6 deletions Regression/Checksum/benchmarks_json/collisionISO.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
"jz": 0.0
},
"electron": {
"particle_momentum_x": 3.5790777034053853e-19,
"particle_momentum_y": 3.5815348106229496e-19,
"particle_momentum_z": 3.577963316718249e-19,
"particle_position_x": 1.024180253191667,
"particle_position_y": 1.023919590453571,
"particle_position_z": 1.0240653505082926,
"particle_momentum_x": 3.578935809964031e-19,
"particle_momentum_y": 3.5778028343192025e-19,
"particle_momentum_z": 3.579884355240226e-19,
"particle_position_x": 1.0241442531780067,
"particle_position_y": 1.0238915904698023,
"particle_position_z": 1.024005350488445,
"particle_weight": 714240000000.0
}
}
20 changes: 10 additions & 10 deletions Regression/Checksum/benchmarks_json/collisionXZ.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@
"Ez": 0.0
},
"ion": {
"particle_momentum_x": 2.5066842316209183e-19,
"particle_momentum_y": 2.2863311215256246e-19,
"particle_momentum_z": 2.2682377973998022e-19,
"particle_position_x": 2656041.6379113654,
"particle_position_y": 2669548.664572591,
"particle_momentum_x": 2.5057703758686855e-19,
"particle_momentum_y": 2.2923783859031756e-19,
"particle_momentum_z": 2.287625547039933e-19,
"particle_position_x": 2668340.55946334,
"particle_position_y": 2656508.8960297015,
"particle_weight": 1.7256099431746894e+26
},
"electron": {
"particle_momentum_x": 1.0390618975838188e-19,
"particle_momentum_y": 1.0241645067704406e-19,
"particle_momentum_z": 1.0173387880649568e-19,
"particle_position_x": 2646162.5818311535,
"particle_position_y": 2661127.0162632912,
"particle_momentum_x": 1.0375566570110091e-19,
"particle_momentum_y": 1.0149787104927666e-19,
"particle_momentum_z": 1.014560693540464e-19,
"particle_position_x": 2649873.564016985,
"particle_position_y": 2662401.3054512525,
"particle_weight": 1.7256099431746894e+26
}
}
10 changes: 5 additions & 5 deletions Regression/Checksum/benchmarks_json/collisionZ.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
"jz": 0.0
},
"ions": {
"particle_momentum_x": 3.424633029669351e-16,
"particle_momentum_y": 3.429026824477811e-16,
"particle_momentum_z": 5.528396735566589e-16,
"particle_position_x": 720.0708684755696,
"particle_weight": 1.0999999999999997e+24
"particle_momentum_x": 3.427967883959658e-16,
"particle_momentum_y": 3.4356969098723214e-16,
"particle_momentum_z": 5.525919423123455e-16,
"particle_position_x": 720.0513313558002,
"particle_weight": 1.0999999999999996e+24
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@
* @param[in] dt is the time step length between two collision calls.
* @param[in] L is the Coulomb log and will be used if greater than zero,
* otherwise will be computed.
* @param[in] dV is the volume of the corresponding cell.
* @param[in] engine the random number generator state & factory
* @param[in] isSameSpecies whether this is an intra-species collision process
* @param[in] coll_idx is the collision index offset.
*/

Expand Down Expand Up @@ -81,6 +83,9 @@ void ElasticCollisionPerez (
// bmax (screening length) cannot be smaller than atomic spacing
const T_PR bmax = amrex::max(lmdD, rmin);

// set max cross section based on mfp = atomic spacing
const T_PR sigma_max = T_PR(1.0) / (maxn * rmin);

#if (defined WARPX_DIM_RZ)
T_PR * const AMREX_RESTRICT theta1 = soa_1.m_rdata[PIdx::theta];
T_PR * const AMREX_RESTRICT theta2 = soa_2.m_rdata[PIdx::theta];
Expand Down Expand Up @@ -114,7 +119,7 @@ void ElasticCollisionPerez (
// scattering path s12 in UpdateMomentumPerezElastic().
// s12 is defined such that the expected value of the change in particle
// velocity is equal to that from the full NxN pairing method, as described
// here https://arxiv.org/submit/5758216/view. This method is a direct extension
// here https://arxiv.org/abs/2407.19151. This method is a direct extension
// of the original method by Takizuka and Abe JCP 25 (1977) to weighted particles.
T_PR n12;
const T_PR wpmax = amrex::max(w1[ I1[i1] ],w2[ I2[i2] ]);
Expand All @@ -124,10 +129,8 @@ void ElasticCollisionPerez (
UpdateMomentumPerezElastic(
u1x[ I1[i1] ], u1y[ I1[i1] ], u1z[ I1[i1] ],
u2x[ I2[i2] ], u2y[ I2[i2] ], u2z[ I2[i2] ],
n1, n2, n12,
q1, m1, w1[ I1[i1] ], q2, m2, w2[ I2[i2] ],
dt, L, bmax,
engine);
n12, sigma_max, L, bmax, dt, engine );

#if (defined WARPX_DIM_RZ)
T_PR const u1xbuf_new = u1x[I1[i1]];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
* @param[in] L is the Coulomb log. A fixed L will be used if L > 0,
* otherwise L will be calculated based on the algorithm.
* @param[in] n12 = max(w1,w2)*min(N1,N2)/dV is the effective density used for s12
* @param[in] sigma_max is the maximum cross section based on mfp = atomic spacing
* used for the normalized scattering length s12 (see Sec. II.C of Perez et al.)
* To see if there are nan or inf updated velocities,
* compile with USE_ASSERTION=TRUE.
*
Expand All @@ -34,11 +36,11 @@ template <typename T_PR, typename T_R>
AMREX_GPU_HOST_DEVICE AMREX_INLINE
void UpdateMomentumPerezElastic (
T_PR& u1x, T_PR& u1y, T_PR& u1z, T_PR& u2x, T_PR& u2y, T_PR& u2z,
T_PR const n1, T_PR const n2, T_PR const n12,
T_PR const q1, T_PR const m1, T_PR const w1,
T_PR const q2, T_PR const m2, T_PR const w2,
T_R const dt, T_PR const L, T_PR const bmax,
amrex::RandomEngine const& engine)
T_PR const n12, T_PR const sigma_max,
T_PR const L, T_PR const bmax,
T_R const dt, amrex::RandomEngine const& engine )
{

T_PR const diffx = amrex::Math::abs(u1x-u2x);
Expand All @@ -64,7 +66,7 @@ void UpdateMomentumPerezElastic (
T_PR const p2y = u2y * m2;
T_PR const p2z = u2z * m2;

// Compute center-of-mass (COM) velocity and gamma
// Compute center-of-momentum (COM) velocity and gamma
T_PR const mass_g = m1 * g1 + m2 * g2;
T_PR const vcx = (p1x+p2x) / mass_g;
T_PR const vcy = (p1y+p2y) / mass_g;
Expand Down Expand Up @@ -103,86 +105,92 @@ void UpdateMomentumPerezElastic (
T_PR const g1s = ( T_PR(1.0) - vcDv1*inv_c2 )*gc*g1;
T_PR const g2s = ( T_PR(1.0) - vcDv2*inv_c2 )*gc*g2;

// Compute s
T_PR s = 0;
// Compute relative velocity in center-of-momentum frame
// (not a Lorentz invariant quantity)
T_PR const muRst = g1s*m1*g2s*m2/(g1s*m1 + g2s*m2);
T_PR const vrelst = p1sm/muRst; // |v1s - v2s|

// Compute invariant relative velocity in center-of-momentum frame
// (Lorentz invariant quantity)
T_PR const denom = T_PR(1.0) + p1sm*p1sm/(m1*g1s*m2*g2s)*inv_c2; // (1.0 - v1s*v2s/c^2)
T_PR const vrelst_invar = vrelst/denom; // |v1s - v2s|/(1.0 - v1s*v2s/c^2)

// Compute s12
T_PR s12 = 0;
if (p1sm > std::numeric_limits<T_PR>::min()) {

// s is non-zero (i.e. particles scatter) only if the relative
// motion between particles is not negligible (p1sm non-zero)
// Writing b0 in a form that is directly analagous to the well-known non-relativistic form.
// See Eq. 3.3.2 in Principles of Plasma Discharges and Material Processing by
// M. A. Lieberman and A. J. Lichtenberg.
// Note that b0 on Eq. 22 of Perez POP 19 (2012) is bmin = b0/2,
// Note: there is a typo in Eq 22 of Perez, the last square is incorrect!
// See the SMILEI documentation: https://smileipic.github.io/Smilei/Understand/collisions.html
// and https://github.com/ECP-WarpX/WarpX/files/3799803/main.pdf from GitHub #429
T_PR const b0 = amrex::Math::abs(q1*q2) /
(T_PR(2.0)*MathConst::pi*PhysConst::ep0*muRst*vrelst*vrelst_invar);


// Compute the Coulomb log lnLmd first
T_PR lnLmd;
if ( L > T_PR(0.0) ) { lnLmd = L; }
else
{
// Compute b0 according to eq (22) from Perez et al., Phys.Plasmas.19.083104 (2012)
// Note: there is a typo in the equation, the last square is incorrect!
// See the SMILEI documentation: https://smileipic.github.io/Smilei/Understand/collisions.html
// and https://github.com/ECP-WarpX/WarpX/files/3799803/main.pdf from GitHub #429
T_PR const b0 = amrex::Math::abs(q1*q2) * inv_c2 /
(T_PR(4.0)*MathConst::pi*PhysConst::ep0) * gc/mass_g *
( m1*g1s*m2*g2s/(p1sm*p1sm*inv_c2) + T_PR(1.0) );

// Compute the minimal impact parameter
constexpr T_PR hbar_pi = static_cast<T_PR>(PhysConst::hbar*MathConst::pi);
const T_PR bmin = amrex::max(hbar_pi/p1sm, b0);

// Compute the minimum impact parameter from quantum: bqm = lDB/(4*pi) = hbar/(2*p1sm)
// See NRL formulary. Also see "An introduction to the physics of the Coulomb logarithm,
// with emphasis on quantum-mechanical effects", J. Plasma Phys. vol. 85 (2019). by J.A. Krommes.
// Note: The formula in Perez 2012 and in Lee and More 1984 uses h rather than
// hbar for bqm. If this is used, then the transition energy where bmin goes from classical
// to quantum is only 2.5 eV for electrons; compared to 100 eV when using hbar.
const T_PR bmin_qm = static_cast<T_PR>(PhysConst::hbar*0.5/p1sm);

// Set the minimum impact parameter
const T_PR bmin = amrex::max(bmin_qm, T_PR(0.5)*b0);

// Compute the Coulomb log lnLmd
lnLmd = amrex::max( T_PR(2.0),
T_PR(0.5)*std::log(T_PR(1.0) + bmax*bmax/(bmin*bmin)) );
}

// Compute s
const auto tts = m1*g1s*m2*g2s/(inv_c2*p1sm*p1sm) + T_PR(1.0);
const auto tts2 = tts*tts;
s = n12 * dt*lnLmd*q1*q1*q2*q2 /
( T_PR(4.0) * MathConst::pi * PhysConst::ep0 * PhysConst::ep0 *
m1*g1*m2*g2/(inv_c2*inv_c2) ) * gc*p1sm/mass_g * tts2;

// Compute s'
const auto cbrt_n1 = std::cbrt(n1);
const auto cbrt_n2 = std::cbrt(n2);
const auto coeff = static_cast<T_PR>(
std::pow(4.0*MathConst::pi/3.0,1.0/3.0));
T_PR const vrel = mass_g*p1sm/(m1*g1s*m2*g2s*gc);
T_PR const sp = coeff * n12 * dt * vrel * (m1+m2) /
amrex::max( m1*cbrt_n1*cbrt_n1,
m2*cbrt_n2*cbrt_n2);

// Determine s
s = amrex::min(s,sp);
// Compute s12 with sigma limited by sigma_max where mfp = atomic spacing
// See https://github.com/user-attachments/files/16555064/CoulombScattering_s12.pdf
// for a proof that this expression for s12 is the same as Eq. 9 of Perez 2012 when
// sigma_eff = pi*b0^2*lnLmd
const T_PR sigma_eff = amrex::min(T_PR(MathConst::pi)*b0*b0*lnLmd,sigma_max);
s12 = sigma_eff * n12 * dt * vrelst * g1s*g2s/(g1*g2);

}

// Only modify momenta if is s is non-zero
if (s > std::numeric_limits<T_PR>::min()) {
// Only modify momenta if s12 is non-zero
if (s12 > std::numeric_limits<T_PR>::min()) {

// Get random numbers
T_PR r = amrex::Random(engine);

// Compute scattering angle
T_PR cosXs;
T_PR sinXs;
if ( s <= T_PR(0.1) )
if ( s12 <= T_PR(0.1) )
{
while ( true )
{
cosXs = T_PR(1.0) + s * std::log(r);
cosXs = T_PR(1.0) + s12 * std::log(r);
// Avoid the bug when r is too small such that cosXs < -1
if ( cosXs >= T_PR(-1.0) ) { break; }
r = amrex::Random(engine);
}
}
else if ( s > T_PR(0.1) && s <= T_PR(3.0) )
else if ( s12 > T_PR(0.1) && s12 <= T_PR(3.0) )
{
T_PR const Ainv = static_cast<T_PR>(
0.0056958 + 0.9560202*s - 0.508139*s*s +
0.47913906*s*s*s - 0.12788975*s*s*s*s + 0.02389567*s*s*s*s*s);
0.0056958 + 0.9560202*s12 - 0.508139*s12*s12 +
0.47913906*s12*s12*s12 - 0.12788975*s12*s12*s12*s12 + 0.02389567*s12*s12*s12*s12*s12);
cosXs = Ainv * std::log( std::exp(T_PR(-1.0)/Ainv) +
T_PR(2.0) * r * std::sinh(T_PR(1.0)/Ainv) );
}
else if ( s > T_PR(3.0) && s <= T_PR(6.0) )
else if ( s12 > T_PR(3.0) && s12 <= T_PR(6.0) )
{
T_PR const A = T_PR(3.0) * std::exp(-s);
T_PR const A = T_PR(3.0) * std::exp(-s12);
cosXs = T_PR(1.0)/A * std::log( std::exp(-A) +
T_PR(2.0) * r * std::sinh(A) );
}
Expand Down

0 comments on commit e56ce8b

Please sign in to comment.