WarpX
Loading...
Searching...
No Matches
SplitAndScatterFunc.H
Go to the documentation of this file.
1/* Copyright 2023-2024 The WarpX Community
2 *
3 * This file is part of WarpX.
4 *
5 * Authors: Roelof Groenewald (TAE Technologies)
6 *
7 * License: BSD-3-Clause-LBNL
8 */
9#ifndef WARPX_SPLIT_AND_SCATTER_FUNC_H_
10#define WARPX_SPLIT_AND_SCATTER_FUNC_H_
11
17#include "Utils/ParticleUtils.H"
19
20#include <array>
21
27{
28 // Define shortcuts for frequently-used type names
31 using ParticleTileDataType = typename ParticleTileType::ParticleTileDataType;
34 using SoaData_type = WarpXParticleContainer::ParticleTileType::ParticleTileDataType;
35
36public:
40 SplitAndScatterFunc () = default;
41
48 SplitAndScatterFunc (const std::string& collision_name, MultiParticleContainer const * mypc);
49
100 const index_type& n_total_pairs,
101 ParticleTileType& ptile0, ParticleTileType& ptile1,
103 ParticleTileType** AMREX_RESTRICT tile_products,
104 const amrex::ParticleReal m0, const amrex::ParticleReal m1,
105 const amrex::Vector<amrex::ParticleReal>& /*products_mass*/,
107 amrex::Vector<index_type>& products_np,
108 const SmartCopy* AMREX_RESTRICT copy_species0,
109 const SmartCopy* AMREX_RESTRICT copy_species1,
110 const index_type* AMREX_RESTRICT p_pair_indices_0,
111 const index_type* AMREX_RESTRICT p_pair_indices_1,
112 const amrex::ParticleReal* AMREX_RESTRICT p_pair_reaction_weight,
113 const amrex::ParticleReal* /*p_product_data*/ ) const
114 {
115 using namespace amrex::literals;
116
117 // Product species slot layout (m_num_product_species slots total):
118 // Slot 0: copy of the first reactant species, i.e. the species from ptile0 (always present)
119 // Slot 1: copy of the other reactant species, i.e. the species from ptile1 (always present)
120 // Slot 2: first true product species
121 // Slot 3: second true product species
122 //
123 // For non-product producing collisions (elastic, back, forward scattering), only slots 0
124 // and 1 receive new macroparticles: one weight-split copy per reactant per colliding pair.
125 // These are counted by `no_product_total` and NOT reflected in `m_num_products_host`.
126 //
127 // For product-producing collisions (ionization, two-product reaction, charge exchange),
128 // slots 2 and 3 receive one new particle per event. For ionization, slot 0 also receives one
129 // particle (the surviving reactant species copy). These counts are stored in
130 // `m_num_products_host[i]` and multiplied by `with_product_total` to get the totals.
131
132 // If there are no colliding pairs, skip the rest of this kernel and return a vector of
133 // zeros, indicating that for all the "product" species, there were no new macroparticles added.
134 if (n_total_pairs == 0) { return amrex::Vector<int>(m_num_product_species, 0); }
135
136 // The following is used to calculate the appropriate offsets for
137 // non-product producing collisions (i.e., not ionization, not two-product reaction).
138 // Note that a standard cummulative sum is not appropriate since the
139 // mask is also used to specify the type of collision and can therefore
140 // have values >1
141 amrex::Gpu::DeviceVector<index_type> no_product_offsets(n_total_pairs);
142 index_type* AMREX_RESTRICT no_product_offsets_data = no_product_offsets.data();
143 auto const no_product_total = amrex::Scan::PrefixSum<index_type>(n_total_pairs,
145 return ((mask[i] > 0) &&
148 (mask[i] != int(ScatteringProcessType::CHARGE_EXCHANGE))) ? 1 : 0;
149 },
150 [=] AMREX_GPU_DEVICE (index_type i, index_type s) { no_product_offsets_data[i] = s; },
152 );
153
155 for (int i = 0; i < 2; i++)
156 {
157 // Record the number of non-product producing collisions that lead to new particles
158 // for the first and second reactant species (slots 0 and 1). Only 1
159 // weight-split particle is created per reactant per event.
160 num_added_vec[i] = static_cast<int>(no_product_total);
161 }
162
163 // The following is used to calculate the appropriate offsets for
164 // product producing processes (i.e., ionization and two-product reaction).
165 // Note that a standard cummulative sum is not appropriate since the
166 // mask is also used to specify the type of collision and can therefore
167 // have values >1
168 amrex::Gpu::DeviceVector<index_type> with_product_offsets(n_total_pairs);
169 index_type* AMREX_RESTRICT with_product_offsets_data = with_product_offsets.data();
170 auto const with_product_total = amrex::Scan::PrefixSum<index_type>(n_total_pairs,
172 return ((mask[i] == int(ScatteringProcessType::IONIZATION)) |
174 (mask[i] == int(ScatteringProcessType::CHARGE_EXCHANGE))) ? 1 : 0;
175 },
176 [=] AMREX_GPU_DEVICE (index_type i, index_type s) { with_product_offsets_data[i] = s; },
178 );
179
180 for (int i = 0; i < m_num_product_species; i++)
181 {
182 // Add the number of product producing events to the species involved in those processes.
183 const int num_products = m_num_products_host[i];
184 const index_type num_added = with_product_total * num_products;
185 num_added_vec[i] += static_cast<int>(num_added);
186 }
187
188 // resize the particle tiles to accomodate the new particles
189 // The code works correctly, even if the same species appears
190 // several times in the product species list.
191 // (e.g., for e- + H -> 2 e- + H+, the product species list is [e-, H, e-, H+])
192 // In that case, the species that appears multiple times is resized several times ;
193 // `products_np` keeps track of array positions at which it has been resized.
194 for (int i = 0; i < m_num_product_species; i++)
195 {
196 products_np[i] = tile_products[i]->numParticles();
197 tile_products[i]->resize(products_np[i] + num_added_vec[i]);
198 }
199
200 const auto soa_0 = ptile0.getParticleTileData();
201 const auto soa_1 = ptile1.getParticleTileData();
202
203 // Create necessary GPU vectors, that will be used in the kernel below
204 amrex::Vector<SoaData_type> soa_products;
205 for (int i = 0; i < m_num_product_species; i++)
206 {
207 soa_products.push_back(tile_products[i]->getParticleTileData());
208 }
209#ifdef AMREX_USE_GPU
212
214 soa_products.end(),
215 device_soa_products.begin());
217 products_np.end(),
218 device_products_np.begin());
219
221 SoaData_type* AMREX_RESTRICT soa_products_data = device_soa_products.data();
222 const index_type* AMREX_RESTRICT products_np_data = device_products_np.data();
223#else
224 SoaData_type* AMREX_RESTRICT soa_products_data = soa_products.data();
225 const index_type* AMREX_RESTRICT products_np_data = products_np.data();
226#endif
227
228 const int num_product_species = m_num_product_species;
229 const auto reaction_energy = m_reaction_energy;
230
231 // Grab the masses of the true product species (slots 2 and 3)
232 amrex::ParticleReal mass_slot2 = 0;
233 amrex::ParticleReal mass_slot3 = 0;
234 if (num_product_species > 2) {
235 mass_slot2 = pc_products[2]->getMass();
236 mass_slot3 = pc_products[3]->getMass();
237 }
238
239 // First perform all non-product producing collisions
240 amrex::ParallelForRNG(n_total_pairs,
241 [=] AMREX_GPU_DEVICE (int i, amrex::RandomEngine const& engine) noexcept
242 {
244 {
245 const auto slot0_idx = products_np_data[0] + no_product_offsets_data[i];
246 // Make a copy of the particle from the first reactant species
247 copy_species0[0](soa_products_data[0], soa_0, static_cast<int>(p_pair_indices_0[i]),
248 static_cast<int>(slot0_idx), engine);
249 // Set the weight of the new particles to p_pair_reaction_weight[i]
250 soa_products_data[0].m_rdata[PIdx::w][slot0_idx] = p_pair_reaction_weight[i];
251
252 const auto slot1_idx = products_np_data[1] + no_product_offsets_data[i];
253 // Make a copy of the particle from the other reactant species
254 copy_species1[1](soa_products_data[1], soa_1, static_cast<int>(p_pair_indices_1[i]),
255 static_cast<int>(slot1_idx), engine);
256 // Set the weight of the new particles to p_pair_reaction_weight[i]
257 soa_products_data[1].m_rdata[PIdx::w][slot1_idx] = p_pair_reaction_weight[i];
258
259 // Set the child particle properties appropriately
260 auto& ux0 = soa_products_data[0].m_rdata[PIdx::ux][slot0_idx];
261 auto& uy0 = soa_products_data[0].m_rdata[PIdx::uy][slot0_idx];
262 auto& uz0 = soa_products_data[0].m_rdata[PIdx::uz][slot0_idx];
263 auto& ux1 = soa_products_data[1].m_rdata[PIdx::ux][slot1_idx];
264 auto& uy1 = soa_products_data[1].m_rdata[PIdx::uy][slot1_idx];
265 auto& uz1 = soa_products_data[1].m_rdata[PIdx::uz][slot1_idx];
266
267 // for simplicity (for now) we assume non-relativistic particles
268 // and simply calculate the center-of-momentum velocity from the
269 // rest masses
270 // TODO: this could be made relativistic by using TwoProductComputeProductMomenta
271 auto const uCOM_x = (m0 * ux0 + m1 * ux1) / (m0 + m1);
272 auto const uCOM_y = (m0 * uy0 + m1 * uy1) / (m0 + m1);
273 auto const uCOM_z = (m0 * uz0 + m1 * uz1) / (m0 + m1);
274
275 // transform to COM frame
276 ux0 -= uCOM_x;
277 uy0 -= uCOM_y;
278 uz0 -= uCOM_z;
279 ux1 -= uCOM_x;
280 uy1 -= uCOM_y;
281 uz1 -= uCOM_z;
282
283 if (mask[i] == int(ScatteringProcessType::ELASTIC)) {
284 // randomly rotate the velocity vector for the first particle
286 ux0, uy0, uz0, std::sqrt(ux0*ux0 + uy0*uy0 + uz0*uz0), engine
287 );
288 // set the second particles velocity so that the total momentum
289 // is zero
290 ux1 = -ux0 * m0 / m1;
291 uy1 = -uy0 * m0 / m1;
292 uz1 = -uz0 * m0 / m1;
293 } else if (mask[i] == int(ScatteringProcessType::BACK)) {
294 // reverse the velocity vectors of both particles
295 ux0 *= -1.0_prt;
296 uy0 *= -1.0_prt;
297 uz0 *= -1.0_prt;
298 ux1 *= -1.0_prt;
299 uy1 *= -1.0_prt;
300 uz1 *= -1.0_prt;
301 } else if (mask[i] == int(ScatteringProcessType::FORWARD)) {
302 amrex::Abort("Forward scattering with DSMC not implemented yet.");
303 }
304 else {
305 amrex::Abort("Unknown scattering process.");
306 }
307 // transform back to labframe
308 ux0 += uCOM_x;
309 uy0 += uCOM_y;
310 uz0 += uCOM_z;
311 ux1 += uCOM_x;
312 uy1 += uCOM_y;
313 uz1 += uCOM_z;
314
315 }
316
317 // Next perform all product-producing collisions
318 else if ((mask[i] == int(ScatteringProcessType::TWOPRODUCT_REACTION)) ||
320 {
321 // create a copy of the first product species at the location of the first reactant species
322 const auto src0_idx = static_cast<int>(p_pair_indices_0[i]);
323 const auto slot2_idx = products_np_data[2] + with_product_offsets_data[i];
324 copy_species0[2](soa_products_data[2], soa_0, src0_idx,
325 static_cast<int>(slot2_idx), engine);
326 // Set the weight of the new particle to p_pair_reaction_weight[i]
327 soa_products_data[2].m_rdata[PIdx::w][slot2_idx] = p_pair_reaction_weight[i];
328
329 // create a copy of the other product species at the location of the other reactant species
330 const auto src1_idx = static_cast<int>(p_pair_indices_1[i]);
331 const auto slot3_idx = products_np_data[3] + with_product_offsets_data[i];
332 copy_species1[3](soa_products_data[3], soa_1, src1_idx,
333 static_cast<int>(slot3_idx), engine);
334 // Set the weight of the new particle to p_pair_reaction_weight[i]
335 soa_products_data[3].m_rdata[PIdx::w][slot3_idx] = p_pair_reaction_weight[i];
336
337 // For charge exchange, swap positions at which the products are created
338 // to ensure local charge conservation. For instance in the reaction
339 // `A+ + B -> A + B+`, B+ will be created at the position of A+, instead of that of B.
341 // The SoA components that define the positions depend on the geometry.
342#if defined(WARPX_DIM_1D_Z)
343 constexpr std::array<int, 1> position_indices = {PIdx::z};
344#elif defined(WARPX_DIM_XZ)
345 constexpr std::array<int, 2> position_indices = {PIdx::x, PIdx::z};
346#elif defined(WARPX_DIM_RZ)
347 constexpr std::array<int, 3> position_indices = {PIdx::r, PIdx::z, PIdx::theta};
348#elif defined(WARPX_DIM_RCYLINDER)
349 constexpr std::array<int, 2> position_indices = {PIdx::r, PIdx::theta};
350#elif defined(WARPX_DIM_RSPHERE)
351 constexpr std::array<int, 3> position_indices = {PIdx::r, PIdx::theta, PIdx::phi};
352#elif defined(WARPX_DIM_3D)
353 constexpr std::array<int, 3> position_indices = {PIdx::x, PIdx::y, PIdx::z};
354#endif
355 // Loop over the components that define the positions and swap the positions of the products.
356 for (int const idim : position_indices) {
357 soa_products_data[2].m_rdata[idim][slot2_idx] = soa_1.m_rdata[idim][src1_idx];
358 soa_products_data[3].m_rdata[idim][slot3_idx] = soa_0.m_rdata[idim][src0_idx];
359 }
360 }
361
362 // Update the momenta of the products
363 TwoProductComputeProductMomenta(
364 soa_0.m_rdata[PIdx::ux][src0_idx],
365 soa_0.m_rdata[PIdx::uy][src0_idx],
366 soa_0.m_rdata[PIdx::uz][src0_idx], m0,
367 soa_1.m_rdata[PIdx::ux][src1_idx],
368 soa_1.m_rdata[PIdx::uy][src1_idx],
369 soa_1.m_rdata[PIdx::uz][src1_idx], m1,
370 soa_products_data[2].m_rdata[PIdx::ux][slot2_idx],
371 soa_products_data[2].m_rdata[PIdx::uy][slot2_idx],
372 soa_products_data[2].m_rdata[PIdx::uz][slot2_idx], mass_slot2,
373 soa_products_data[3].m_rdata[PIdx::ux][slot3_idx],
374 soa_products_data[3].m_rdata[PIdx::uy][slot3_idx],
375 soa_products_data[3].m_rdata[PIdx::uz][slot3_idx], mass_slot3,
376 -reaction_energy*PhysConst::q_e,
377 // TwoProductComputeProductMomenta expects the *released* energy here, hence the negative sign
378 // We also convert from eV to Joules
380 // no angular scattering: the products' momenta have the same direction
381 // as that of the reactant particle, in the center-of-mass frame
382 engine);
383 }
384 else if (mask[i] == int(ScatteringProcessType::IONIZATION))
385 {
386 const auto slot0_idx = products_np_data[0] + no_product_total + with_product_offsets_data[i];
387 // Make a copy of the particle from the first reactant species (slot 0)
388 copy_species0[0](soa_products_data[0], soa_0, static_cast<int>(p_pair_indices_0[i]),
389 static_cast<int>(slot0_idx), engine);
390 // Set the weight of the new particles to p_pair_reaction_weight[i]
391 soa_products_data[0].m_rdata[PIdx::w][slot0_idx] = p_pair_reaction_weight[i];
392
393 // create a copy of the first product species at the location of the other reactant species
394 // (the other reactant species in slot 1 is the "target species", i.e. the species that is ionized)
395 const auto slot2_idx = products_np_data[2] + with_product_offsets_data[i];
396 copy_species1[2](soa_products_data[2], soa_1, static_cast<int>(p_pair_indices_1[i]),
397 static_cast<int>(slot2_idx), engine);
398 // Set the weight of the new particle to p_pair_reaction_weight[i]
399 soa_products_data[2].m_rdata[PIdx::w][slot2_idx] = p_pair_reaction_weight[i];
400
401 // create a copy of the other product species at the location of the other reactant species
402 // (the other reactant species in slot 1 is the "target species", i.e. the species that is ionized)
403 const auto slot3_idx = products_np_data[3] + with_product_offsets_data[i];
404 copy_species1[3](soa_products_data[3], soa_1, static_cast<int>(p_pair_indices_1[i]),
405 static_cast<int>(slot3_idx), engine);
406 // Set the weight of the new particle to p_pair_reaction_weight[i]
407 soa_products_data[3].m_rdata[PIdx::w][slot3_idx] = p_pair_reaction_weight[i];
408
409 // Grab the colliding particle velocities to calculate the COM
410 // Note that the two product particles currently have the same
411 // velocity as the "target" particle
412 auto& ux0 = soa_products_data[0].m_rdata[PIdx::ux][slot0_idx];
413 auto& uy0 = soa_products_data[0].m_rdata[PIdx::uy][slot0_idx];
414 auto& uz0 = soa_products_data[0].m_rdata[PIdx::uz][slot0_idx];
415 auto& ux2 = soa_products_data[2].m_rdata[PIdx::ux][slot2_idx];
416 auto& uy2 = soa_products_data[2].m_rdata[PIdx::uy][slot2_idx];
417 auto& uz2 = soa_products_data[2].m_rdata[PIdx::uz][slot2_idx];
418 auto& ux3 = soa_products_data[3].m_rdata[PIdx::ux][slot3_idx];
419 auto& uy3 = soa_products_data[3].m_rdata[PIdx::uy][slot3_idx];
420 auto& uz3 = soa_products_data[3].m_rdata[PIdx::uz][slot3_idx];
421
422#if defined(WARPX_DIM_RZ) || defined(WARPX_DIM_RCYLINDER)
423 /* In RZ and RCYLINDER geometry, macroparticles can collide with other macroparticles
424 * in the same *cylindrical* cell. For this reason, collisions between macroparticles
425 * are actually not local in space. In this case, the underlying assumption is that
426 * particles within the same cylindrical cell represent a cylindrically-symmetry
427 * momentum distribution function. Therefore, here, we temporarily rotate the
428 * momentum of one of the macroparticles in agreement with this cylindrical symmetry.
429 * (This is technically only valid if we use only the m=0 azimuthal mode in the simulation;
430 * there is a corresponding assert statement at initialization.)
431 */
432 amrex::ParticleReal const theta = (
433 soa_products_data[2].m_rdata[PIdx::theta][slot2_idx]
434 - soa_products_data[0].m_rdata[PIdx::theta][slot0_idx]
435 );
436 amrex::ParticleReal const ux0buf = ux0;
437 ux0 = ux0buf*std::cos(theta) - uy0*std::sin(theta);
438 uy0 = ux0buf*std::sin(theta) + uy0*std::cos(theta);
439#endif
440
441 // for simplicity (for now) we assume non-relativistic particles
442 // and simply calculate the center-of-momentum velocity from the
443 // rest masses
444 auto const uCOM_x = (m0 * ux0 + m1 * ux3) / (m0 + m1);
445 auto const uCOM_y = (m0 * uy0 + m1 * uy3) / (m0 + m1);
446 auto const uCOM_z = (m0 * uz0 + m1 * uz3) / (m0 + m1);
447
448 // transform to COM frame
449 ux0 -= uCOM_x;
450 uy0 -= uCOM_y;
451 uz0 -= uCOM_z;
452 ux2 -= uCOM_x;
453 uy2 -= uCOM_y;
454 uz2 -= uCOM_z;
455 ux3 -= uCOM_x;
456 uy3 -= uCOM_y;
457 uz3 -= uCOM_z;
458
459 // calculate kinetic energy of the collision
460 const amrex::ParticleReal p_non_target_in = m0 * std::sqrt(ux0*ux0 + uy0*uy0 + uz0*uz0);
461 const amrex::ParticleReal E_coll = 0.5_prt * p_non_target_in * p_non_target_in * (1.0_prt/m0 + 1.0_prt/m1);
462 // subtract the energy cost for ionization (converted from eV to J)
463 const amrex::ParticleReal E_out = E_coll - reaction_energy * PhysConst::q_e;
464
465 // The kinematics of the ionization event (i.e. how the energy and momentum are
466 // distributed among the products) depend on the nature of the incident particle
467 // (first reactant species, slot 0, with mass m0).
468 if (m0 > 2.0_prt * PhysConst::m_e)
469 {
470 // Ion impact ionization (the incident particle is heavier than an electron).
471 // We use the collinear model: in the center-of-mass frame, the two products
472 // (the ion and the electron) are assumed to have equal velocity, and their
473 // velocity vector is aligned with that of the incident (non-target) particle.
474
475 // Momentum and energy division after the ionization event is done as follows:
476 // we assume that, in the center of mass frame, the two products (the ion and the electron)
477 // have equal velocity and that their velocity vector is aligned with that of the target particle
478
479 // With these assumptions, conservation of momentum in the center of mass frame allows to express the momenta
480 // of the products, as a function of the outcoming momentum of the non-target particle:
481 // p2 = - p_non_target_out * mass_slot2 / m1
482 // p3 = - p_non_target_out * mass_slot3 / m1
483 // (with m1 = mass_slot2 + mass_slot3, by conservation of mass)
484
485 // Amplitude of the momentum of the non-target particle, obtained from conservation of energy
486 const amrex::ParticleReal p_non_target_out = std::sqrt( 2.0_prt * E_out / (1.0_prt / m0 + 1.0_prt / (mass_slot2 + mass_slot3) ) );
487
488 // Reduce the velocity of the non-target particle, to reflect the loss of energy due to ionization
489 const amrex::ParticleReal reduce_factor = p_non_target_out / p_non_target_in;
490 ux0 *= reduce_factor;
491 uy0 *= reduce_factor;
492 uz0 *= reduce_factor;
493 // Obtain the other velocity vectors by conservation of momentum
494 const amrex::ParticleReal mass_ratio = m0 / (mass_slot2 + mass_slot3);
495 ux2 = -mass_ratio * ux0;
496 uy2 = -mass_ratio * uy0;
497 uz2 = -mass_ratio * uz0;
498 ux3 = -mass_ratio * ux0;
499 uy3 = -mass_ratio * uy0;
500 uz3 = -mass_ratio * uz0;
501 }
502 else
503 {
504 // Electron impact ionization (the incident particle is an electron).
505 // We use the following model: in the center-of-mass frame, the two
506 // electrons (the incident electron and the newly freed electron)
507 // are scattered isotropically and are assumed to carry an equal share
508 // of the energy. All the other unknowns (magnitude of the isotropically
509 // scattered electron momentum, momentum of the ion) are determined by
510 // conservation of energy and momentum.
511
512 // The freed electron has the same mass as the incident electron (m0);
513 // the ion is the heavier of the two products in slots 2 and 3.
514 const amrex::ParticleReal m_ion = amrex::max(mass_slot2, mass_slot3);
515
516 // Sample two isotropic unit vectors: one for the incident electron
517 // (slot 0) and one for the newly freed electron.
518 // These units vectors have the right direction, but not the right magnitude ;
519 // they will be multiplied by the right magnitude p_e further down.
520 amrex::ParticleReal scaled_ux0, scaled_uy0, scaled_uz0;
521 amrex::ParticleReal scaled_ux_new, scaled_uy_new, scaled_uz_new;
522 ParticleUtils::getRandomVector(scaled_ux0, scaled_uy0, scaled_uz0, engine);
523 ParticleUtils::getRandomVector(scaled_ux_new, scaled_uy_new, scaled_uz_new, engine);
524
525 // Sum of the two electron directions; the ion momentum is
526 // proportional to its opposite (conservation of momentum in COM).
527 const amrex::ParticleReal scaled_ux_ion = -(scaled_ux0 + scaled_ux_new);
528 const amrex::ParticleReal scaled_uy_ion = -(scaled_uy0 + scaled_uy_new);
529 const amrex::ParticleReal scaled_uz_ion = -(scaled_uz0 + scaled_uz_new);
530 const amrex::ParticleReal scaled_u2_ion = scaled_ux_ion*scaled_ux_ion + scaled_uy_ion*scaled_uy_ion + scaled_uz_ion*scaled_uz_ion;
531
532 // Magnitude of the momentum of the two electrons, from conservation
533 // of energy in the center-of-mass frame (both electrons are assumed
534 // to carry an equal share of the energy):
535 // 2 * p_e^2 / (2 m_e) + p_ion^2 / (2 m_ion) = E_out
536 const amrex::ParticleReal p_e = std::sqrt(
537 E_out / (1.0_prt/m0 + 0.5_prt*scaled_u2_ion/m_ion) );
538
539 // Assign the normalized momenta (still in the COM frame; the surrounding code
540 // transforms them back to the lab frame). The incident electron is in
541 // slot 0; the two products of the ionized target (slots 2 and 3) are
542 // one electron and one ion, the electron being the lighter of the two.
543 ux0 = p_e/m0 * scaled_ux0;
544 uy0 = p_e/m0 * scaled_uy0;
545 uz0 = p_e/m0 * scaled_uz0;
546 if (mass_slot2 < mass_slot3) {
547 // slot 2 is the freed electron, slot 3 is the ion
548 ux2 = p_e/m0 * scaled_ux_new;
549 uy2 = p_e/m0 * scaled_uy_new;
550 uz2 = p_e/m0 * scaled_uz_new;
551 ux3 = p_e/m_ion * scaled_ux_ion;
552 uy3 = p_e/m_ion * scaled_uy_ion;
553 uz3 = p_e/m_ion * scaled_uz_ion;
554 } else {
555 // slot 3 is the freed electron, slot 2 is the ion
556 ux3 = p_e/m0 * scaled_ux_new;
557 uy3 = p_e/m0 * scaled_uy_new;
558 uz3 = p_e/m0 * scaled_uz_new;
559 ux2 = p_e/m_ion * scaled_ux_ion;
560 uy2 = p_e/m_ion * scaled_uy_ion;
561 uz2 = p_e/m_ion * scaled_uz_ion;
562 }
563 }
564
565 // transform back to labframe
566 ux0 += uCOM_x;
567 uy0 += uCOM_y;
568 uz0 += uCOM_z;
569 ux2 += uCOM_x;
570 uy2 += uCOM_y;
571 uz2 += uCOM_z;
572 ux3 += uCOM_x;
573 uy3 += uCOM_y;
574 uz3 += uCOM_z;
575
576#if defined(WARPX_DIM_RZ) || defined(WARPX_DIM_RCYLINDER)
577 /* Undo the earlier velocity rotation. */
578 amrex::ParticleReal const ux0buf_new = ux0;
579 ux0 = ux0buf_new*std::cos(-theta) - uy0*std::sin(-theta);
580 uy0 = ux0buf_new*std::sin(-theta) + uy0*std::cos(-theta);
581#endif
582 }
583 });
584
585 // Initialize the user runtime components
586 for (int i = 0; i < m_num_product_species; i++)
587 {
588 const int start_index = int(products_np[i]);
589 const int stop_index = int(products_np[i] + num_added_vec[i]);
591 *pc_products[i], start_index, stop_index);
592 }
593
595 return num_added_vec;
596 }
597
598private:
599 // How many different type of species the collision produces
601 // If ionization collisions are included, what is the energy cost
603 // Vector of size m_num_product_species: for each product slot, the number of new particles
604 // created per *reaction* event (ionization, two-product reaction, charge exchange).
605 // Weight-split particles added to slots 0 and 1 during non-reaction events are NOT counted
606 // here; they are tracked separately via `no_product_total` inside operator().
609};
610#endif // WARPX_SPLIT_AND_SCATTER_FUNC_H_
#define AMREX_RESTRICT
#define AMREX_INLINE
#define AMREX_GPU_DEVICE
Array4< int const > mask
CollisionType
Definition BinaryCollisionUtils.H:17
@ BACK
Definition ScatteringProcess.H:23
@ CHARGE_EXCHANGE
Definition ScatteringProcess.H:25
@ ELASTIC
Definition ScatteringProcess.H:22
@ TWOPRODUCT_REACTION
Definition ScatteringProcess.H:24
@ IONIZATION
Definition ScatteringProcess.H:27
@ FORWARD
Definition ScatteringProcess.H:28
@ Forward
Definition WarpXAlgorithmSelection.H:198
Definition MultiParticleContainer.H:69
typename ParticleBins::index_type index_type
Definition SplitAndScatterFunc.H:33
typename ParticleTileType::ParticleTileDataType ParticleTileDataType
Definition SplitAndScatterFunc.H:31
amrex::ParticleReal m_reaction_energy
Definition SplitAndScatterFunc.H:602
int m_num_product_species
Definition SplitAndScatterFunc.H:600
SplitAndScatterFunc()=default
Default constructor of the SplitAndScatterFunc class.
typename WarpXParticleContainer::ParticleType ParticleType
Definition SplitAndScatterFunc.H:29
CollisionType m_collision_type
Definition SplitAndScatterFunc.H:608
amrex::DenseBins< ParticleTileDataType > ParticleBins
Definition SplitAndScatterFunc.H:32
typename WarpXParticleContainer::ParticleTileType ParticleTileType
Definition SplitAndScatterFunc.H:30
AMREX_INLINE amrex::Vector< int > operator()(const index_type &n_total_pairs, ParticleTileType &ptile0, ParticleTileType &ptile1, const amrex::Vector< WarpXParticleContainer * > &pc_products, ParticleTileType **AMREX_RESTRICT tile_products, const amrex::ParticleReal m0, const amrex::ParticleReal m1, const amrex::Vector< amrex::ParticleReal > &, const index_type *AMREX_RESTRICT mask, amrex::Vector< index_type > &products_np, const SmartCopy *AMREX_RESTRICT copy_species0, const SmartCopy *AMREX_RESTRICT copy_species1, const index_type *AMREX_RESTRICT p_pair_indices_0, const index_type *AMREX_RESTRICT p_pair_indices_1, const amrex::ParticleReal *AMREX_RESTRICT p_pair_reaction_weight, const amrex::ParticleReal *) const
Function that performs binary collision between macroparticle pairs (referred to as "reactant species...
Definition SplitAndScatterFunc.H:99
amrex::Gpu::HostVector< int > m_num_products_host
Definition SplitAndScatterFunc.H:607
WarpXParticleContainer::ParticleTileType::ParticleTileDataType SoaData_type
Definition SplitAndScatterFunc.H:34
iterator begin() noexcept
T * data() noexcept
std::conditional_t< is_rtsoa_pc, ParticleTileRT< typename ParticleType::RealType, typename ParticleType::IntType >, ParticleTile< ParticleType, NArrayReal, NArrayInt, Allocator > > ParticleTileType
amrex_particle_real ParticleReal
PinnedVector< T > HostVector
PODVector< T, ArenaAllocator< T > > DeviceVector
void DefaultInitializeRuntimeAttributes(PTile &ptile, const DstPC &pc, int start, int stop, const int n_external_attr_real=0, const int n_external_attr_int=0, const bool do_qed_comps=false)
Default initialize runtime attributes in a tile. This routine does not initialize the first n_externa...
Definition DefaultInitialization.H:114
AMREX_GPU_HOST_DEVICE AMREX_INLINE void RandomizeVelocity(amrex::ParticleReal &ux, amrex::ParticleReal &uy, amrex::ParticleReal &uz, const amrex::ParticleReal vp, amrex::RandomEngine const &engine)
Function to perform scattering of a particle that results in a random velocity vector with given magn...
Definition ParticleUtils.H:218
AMREX_GPU_HOST_DEVICE AMREX_INLINE void getRandomVector(amrex::ParticleReal &x, amrex::ParticleReal &y, amrex::ParticleReal &z, amrex::RandomEngine const &engine)
Generate a random unit vector with isotropic distribution, following the method described in equation...
Definition ParticleUtils.H:195
constexpr auto m_e
electron mass [kg]
Definition constant.H:172
constexpr auto q_e
elementary charge [C]
Definition constant.H:169
void synchronize() noexcept
void copyAsync(HostToDevice, InIter begin, InIter end, OutIter result) noexcept
static constexpr HostToDevice hostToDevice
void streamSynchronize() noexcept
static constexpr struct amrex::Scan::Type::Exclusive exclusive
static constexpr RetSum retSum
T PrefixSum(N n, FIN const &fin, FOUT const &fout, TYPE, RetSum a_ret_sum=retSum)
__host__ __device__ constexpr const T & max(const T &a, const T &b) noexcept
AMREX_ATTRIBUTE_FLATTEN_FOR void ParallelForRNG(T n, L const &f) noexcept
void Abort(const std::string &msg)
const int[]
@ x
Definition WarpXParticleContainer.H:70
@ uz
Definition WarpXParticleContainer.H:70
@ w
Definition WarpXParticleContainer.H:70
@ uy
Definition WarpXParticleContainer.H:70
@ z
Definition WarpXParticleContainer.H:70
@ ux
Definition WarpXParticleContainer.H:70
This is a functor for performing a "smart copy" that works in both host and device code.
Definition SmartCopy.H:34