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
25{
26 // Define shortcuts for frequently-used type names
29 using ParticleTileDataType = typename ParticleTileType::ParticleTileDataType;
32 using SoaData_type = WarpXParticleContainer::ParticleTileType::ParticleTileDataType;
33
34public:
38 SplitAndScatterFunc () = default;
39
46 SplitAndScatterFunc (const std::string& collision_name, MultiParticleContainer const * mypc);
47
98 const index_type& n_total_pairs,
99 ParticleTileType& ptile0, ParticleTileType& ptile1,
101 ParticleTileType** AMREX_RESTRICT tile_products,
102 const amrex::ParticleReal m0, const amrex::ParticleReal m1,
103 const amrex::Vector<amrex::ParticleReal>& /*products_mass*/,
105 amrex::Vector<index_type>& products_np,
106 const SmartCopy* AMREX_RESTRICT copy_species0,
107 const SmartCopy* AMREX_RESTRICT copy_species1,
108 const index_type* AMREX_RESTRICT p_pair_indices_0,
109 const index_type* AMREX_RESTRICT p_pair_indices_1,
110 const amrex::ParticleReal* AMREX_RESTRICT p_pair_reaction_weight,
111 const amrex::ParticleReal* /*p_product_data*/ ) const
112 {
113 using namespace amrex::literals;
114
115 // Product species slot layout (m_num_product_species slots total):
116 // Slot 0: copy of the first reactant species, i.e. the species from ptile0 (always present)
117 // Slot 1: copy of the other reactant species, i.e. the species from ptile1 (always present)
118 // Slot 2: first true product species
119 // Slot 3: second true product species
120 //
121 // For non-product producing collisions (elastic, back, forward scattering), only slots 0
122 // and 1 receive new macroparticles: one weight-split copy per reactant per colliding pair.
123 // These are counted by `no_product_total` and NOT reflected in `m_num_products_host`.
124 //
125 // For product-producing collisions (ionization, two-product reaction, charge exchange),
126 // slots 2 and 3 receive one new particle per event. For ionization, slot 0 also receives one
127 // particle (the surviving reactant species copy). These counts are stored in
128 // `m_num_products_host[i]` and multiplied by `with_product_total` to get the totals.
129
130 // If there are no colliding pairs, skip the rest of this kernel and return a vector of
131 // zeros, indicating that for all the "product" species, there were no new macroparticles added.
132 if (n_total_pairs == 0) { return amrex::Vector<int>(m_num_product_species, 0); }
133
134 // The following is used to calculate the appropriate offsets for
135 // non-product producing collisions (i.e., not ionization, not two-product reaction).
136 // Note that a standard cummulative sum is not appropriate since the
137 // mask is also used to specify the type of collision and can therefore
138 // have values >1
139 amrex::Gpu::DeviceVector<index_type> no_product_offsets(n_total_pairs);
140 index_type* AMREX_RESTRICT no_product_offsets_data = no_product_offsets.data();
141 auto const no_product_total = amrex::Scan::PrefixSum<index_type>(n_total_pairs,
143 return ((mask[i] > 0) &
146 },
147 [=] AMREX_GPU_DEVICE (index_type i, index_type s) { no_product_offsets_data[i] = s; },
149 );
150
152 for (int i = 0; i < 2; i++)
153 {
154 // Record the number of non-product producing collisions that lead to new particles
155 // for the first and second reactant species (slots 0 and 1). Only 1
156 // weight-split particle is created per reactant per event.
157 num_added_vec[i] = static_cast<int>(no_product_total);
158 }
159
160 // The following is used to calculate the appropriate offsets for
161 // product producing processes (i.e., ionization and two-product reaction).
162 // Note that a standard cummulative sum is not appropriate since the
163 // mask is also used to specify the type of collision and can therefore
164 // have values >1
165 amrex::Gpu::DeviceVector<index_type> with_product_offsets(n_total_pairs);
166 index_type* AMREX_RESTRICT with_product_offsets_data = with_product_offsets.data();
167 auto const with_product_total = amrex::Scan::PrefixSum<index_type>(n_total_pairs,
169 return ((mask[i] == int(ScatteringProcessType::IONIZATION)) |
171 },
172 [=] AMREX_GPU_DEVICE (index_type i, index_type s) { with_product_offsets_data[i] = s; },
174 );
175
176 for (int i = 0; i < m_num_product_species; i++)
177 {
178 // Add the number of product producing events to the species involved in those processes.
179 const int num_products = m_num_products_host[i];
180 const index_type num_added = with_product_total * num_products;
181 num_added_vec[i] += static_cast<int>(num_added);
182 }
183
184 // resize the particle tiles to accomodate the new particles
185 // The code works correctly, even if the same species appears
186 // several times in the product species list.
187 // (e.g., for e- + H -> 2 e- + H+, the product species list is [e-, H, e-, H+])
188 // In that case, the species that appears multiple times is resized several times ;
189 // `products_np` keeps track of array positions at which it has been resized.
190 for (int i = 0; i < m_num_product_species; i++)
191 {
192 products_np[i] = tile_products[i]->numParticles();
193 tile_products[i]->resize(products_np[i] + num_added_vec[i]);
194 }
195
196 const auto soa_0 = ptile0.getParticleTileData();
197 const auto soa_1 = ptile1.getParticleTileData();
198
199 // Create necessary GPU vectors, that will be used in the kernel below
200 amrex::Vector<SoaData_type> soa_products;
201 for (int i = 0; i < m_num_product_species; i++)
202 {
203 soa_products.push_back(tile_products[i]->getParticleTileData());
204 }
205#ifdef AMREX_USE_GPU
208
210 soa_products.end(),
211 device_soa_products.begin());
213 products_np.end(),
214 device_products_np.begin());
215
217 SoaData_type* AMREX_RESTRICT soa_products_data = device_soa_products.data();
218 const index_type* AMREX_RESTRICT products_np_data = device_products_np.data();
219#else
220 SoaData_type* AMREX_RESTRICT soa_products_data = soa_products.data();
221 const index_type* AMREX_RESTRICT products_np_data = products_np.data();
222#endif
223
224 const int num_product_species = m_num_product_species;
225 const auto reaction_energy = m_reaction_energy;
226
227 // Grab the masses of the true product species (slots 2 and 3)
228 amrex::ParticleReal mass_slot2 = 0;
229 amrex::ParticleReal mass_slot3 = 0;
230 if (num_product_species > 2) {
231 mass_slot2 = pc_products[2]->getMass();
232 mass_slot3 = pc_products[3]->getMass();
233 }
234
235 // First perform all non-product producing collisions
236 amrex::ParallelForRNG(n_total_pairs,
237 [=] AMREX_GPU_DEVICE (int i, amrex::RandomEngine const& engine) noexcept
238 {
240 {
241 const auto slot0_idx = products_np_data[0] + no_product_offsets_data[i];
242 // Make a copy of the particle from the first reactant species
243 copy_species0[0](soa_products_data[0], soa_0, static_cast<int>(p_pair_indices_0[i]),
244 static_cast<int>(slot0_idx), engine);
245 // Set the weight of the new particles to p_pair_reaction_weight[i]
246 soa_products_data[0].m_rdata[PIdx::w][slot0_idx] = p_pair_reaction_weight[i];
247
248 const auto slot1_idx = products_np_data[1] + no_product_offsets_data[i];
249 // Make a copy of the particle from the other reactant species
250 copy_species1[1](soa_products_data[1], soa_1, static_cast<int>(p_pair_indices_1[i]),
251 static_cast<int>(slot1_idx), engine);
252 // Set the weight of the new particles to p_pair_reaction_weight[i]
253 soa_products_data[1].m_rdata[PIdx::w][slot1_idx] = p_pair_reaction_weight[i];
254
255 // Set the child particle properties appropriately
256 auto& ux0 = soa_products_data[0].m_rdata[PIdx::ux][slot0_idx];
257 auto& uy0 = soa_products_data[0].m_rdata[PIdx::uy][slot0_idx];
258 auto& uz0 = soa_products_data[0].m_rdata[PIdx::uz][slot0_idx];
259 auto& ux1 = soa_products_data[1].m_rdata[PIdx::ux][slot1_idx];
260 auto& uy1 = soa_products_data[1].m_rdata[PIdx::uy][slot1_idx];
261 auto& uz1 = soa_products_data[1].m_rdata[PIdx::uz][slot1_idx];
262
263#if defined(WARPX_DIM_RZ) || defined(WARPX_DIM_RCYLINDER)
264 /* In RZ and RCYLINDER geometry, macroparticles can collide with other macroparticles
265 * in the same *cylindrical* cell. For this reason, collisions between macroparticles
266 * are actually not local in space. In this case, the underlying assumption is that
267 * particles within the same cylindrical cell represent a cylindrically-symmetry
268 * momentum distribution function. Therefore, here, we temporarily rotate the
269 * momentum of one of the macroparticles in agreement with this cylindrical symmetry.
270 * (This is technically only valid if we use only the m=0 azimuthal mode in the simulation;
271 * there is a corresponding assert statement at initialization.)
272 */
273 amrex::ParticleReal const theta = (
274 soa_products_data[1].m_rdata[PIdx::theta][slot1_idx]
275 - soa_products_data[0].m_rdata[PIdx::theta][slot0_idx]
276 );
277 amrex::ParticleReal const ux0buf = ux0;
278 ux0 = ux0buf*std::cos(theta) - uy0*std::sin(theta);
279 uy0 = ux0buf*std::sin(theta) + uy0*std::cos(theta);
280#endif
281
282 // for simplicity (for now) we assume non-relativistic particles
283 // and simply calculate the center-of-momentum velocity from the
284 // rest masses
285 // TODO: this could be made relativistic by using TwoProductComputeProductMomenta
286 auto const uCOM_x = (m0 * ux0 + m1 * ux1) / (m0 + m1);
287 auto const uCOM_y = (m0 * uy0 + m1 * uy1) / (m0 + m1);
288 auto const uCOM_z = (m0 * uz0 + m1 * uz1) / (m0 + m1);
289
290 // transform to COM frame
291 ux0 -= uCOM_x;
292 uy0 -= uCOM_y;
293 uz0 -= uCOM_z;
294 ux1 -= uCOM_x;
295 uy1 -= uCOM_y;
296 uz1 -= uCOM_z;
297
298 if (mask[i] == int(ScatteringProcessType::ELASTIC)) {
299 // randomly rotate the velocity vector for the first particle
301 ux0, uy0, uz0, std::sqrt(ux0*ux0 + uy0*uy0 + uz0*uz0), engine
302 );
303 // set the second particles velocity so that the total momentum
304 // is zero
305 ux1 = -ux0 * m0 / m1;
306 uy1 = -uy0 * m0 / m1;
307 uz1 = -uz0 * m0 / m1;
308 } else if (mask[i] == int(ScatteringProcessType::BACK)) {
309 // reverse the velocity vectors of both particles
310 ux0 *= -1.0_prt;
311 uy0 *= -1.0_prt;
312 uz0 *= -1.0_prt;
313 ux1 *= -1.0_prt;
314 uy1 *= -1.0_prt;
315 uz1 *= -1.0_prt;
316 } else if (mask[i] == int(ScatteringProcessType::FORWARD)) {
317 amrex::Abort("Forward scattering with DSMC not implemented yet.");
318 }
319 else {
320 amrex::Abort("Unknown scattering process.");
321 }
322 // transform back to labframe
323 ux0 += uCOM_x;
324 uy0 += uCOM_y;
325 uz0 += uCOM_z;
326 ux1 += uCOM_x;
327 uy1 += uCOM_y;
328 uz1 += uCOM_z;
329
330#if defined(WARPX_DIM_RZ) || defined(WARPX_DIM_RCYLINDER)
331 /* Undo the earlier velocity rotation. */
332 amrex::ParticleReal const ux0buf_new = ux0;
333 ux0 = ux0buf_new*std::cos(-theta) - uy0*std::sin(-theta);
334 uy0 = ux0buf_new*std::sin(-theta) + uy0*std::cos(-theta);
335#endif
336 }
337
338 // Next perform all product-producing collisions
340 {
341 // create a copy of the first product species at the location of the first reactant species
342 const auto src0_idx = static_cast<int>(p_pair_indices_0[i]);
343 const auto slot2_idx = products_np_data[2] + with_product_offsets_data[i];
344 copy_species0[2](soa_products_data[2], soa_0, src0_idx,
345 static_cast<int>(slot2_idx), engine);
346 // Set the weight of the new particle to p_pair_reaction_weight[i]
347 soa_products_data[2].m_rdata[PIdx::w][slot2_idx] = p_pair_reaction_weight[i];
348
349 // create a copy of the other product species at the location of the other reactant species
350 const auto src1_idx = static_cast<int>(p_pair_indices_1[i]);
351 const auto slot3_idx = products_np_data[3] + with_product_offsets_data[i];
352 copy_species1[3](soa_products_data[3], soa_1, src1_idx,
353 static_cast<int>(slot3_idx), engine);
354 // Set the weight of the new particle to p_pair_reaction_weight[i]
355 soa_products_data[3].m_rdata[PIdx::w][slot3_idx] = p_pair_reaction_weight[i];
356
357 // Update the momenta of the products
358 TwoProductComputeProductMomenta(
359 soa_0.m_rdata[PIdx::ux][src0_idx],
360 soa_0.m_rdata[PIdx::uy][src0_idx],
361 soa_0.m_rdata[PIdx::uz][src0_idx], m0,
362 soa_1.m_rdata[PIdx::ux][src1_idx],
363 soa_1.m_rdata[PIdx::uy][src1_idx],
364 soa_1.m_rdata[PIdx::uz][src1_idx], m1,
365 soa_products_data[2].m_rdata[PIdx::ux][slot2_idx],
366 soa_products_data[2].m_rdata[PIdx::uy][slot2_idx],
367 soa_products_data[2].m_rdata[PIdx::uz][slot2_idx], mass_slot2,
368 soa_products_data[3].m_rdata[PIdx::ux][slot3_idx],
369 soa_products_data[3].m_rdata[PIdx::uy][slot3_idx],
370 soa_products_data[3].m_rdata[PIdx::uz][slot3_idx], mass_slot3,
371 -reaction_energy*PhysConst::q_e,
372 // TwoProductComputeProductMomenta expects the *released* energy here, hence the negative sign
373 // We also convert from eV to Joules
375 // no angular scattering: the products' momenta have the same direction
376 // as that of the reactant particle, in the center-of-mass frame
377 engine);
378 }
379 else if (mask[i] == int(ScatteringProcessType::IONIZATION))
380 {
381 const auto slot0_idx = products_np_data[0] + no_product_total + with_product_offsets_data[i];
382 // Make a copy of the particle from the first reactant species (slot 0)
383 copy_species0[0](soa_products_data[0], soa_0, static_cast<int>(p_pair_indices_0[i]),
384 static_cast<int>(slot0_idx), engine);
385 // Set the weight of the new particles to p_pair_reaction_weight[i]
386 soa_products_data[0].m_rdata[PIdx::w][slot0_idx] = p_pair_reaction_weight[i];
387
388 // create a copy of the first product species at the location of the other reactant species
389 // (the other reactant species in slot 1 is the "target species", i.e. the species that is ionized)
390 const auto slot2_idx = products_np_data[2] + with_product_offsets_data[i];
391 copy_species1[2](soa_products_data[2], soa_1, static_cast<int>(p_pair_indices_1[i]),
392 static_cast<int>(slot2_idx), engine);
393 // Set the weight of the new particle to p_pair_reaction_weight[i]
394 soa_products_data[2].m_rdata[PIdx::w][slot2_idx] = p_pair_reaction_weight[i];
395
396 // create a copy of the other product species at the location of the other reactant species
397 // (the other reactant species in slot 1 is the "target species", i.e. the species that is ionized)
398 const auto slot3_idx = products_np_data[3] + with_product_offsets_data[i];
399 copy_species1[3](soa_products_data[3], soa_1, static_cast<int>(p_pair_indices_1[i]),
400 static_cast<int>(slot3_idx), engine);
401 // Set the weight of the new particle to p_pair_reaction_weight[i]
402 soa_products_data[3].m_rdata[PIdx::w][slot3_idx] = p_pair_reaction_weight[i];
403
404 // Grab the colliding particle velocities to calculate the COM
405 // Note that the two product particles currently have the same
406 // velocity as the "target" particle
407 auto& ux0 = soa_products_data[0].m_rdata[PIdx::ux][slot0_idx];
408 auto& uy0 = soa_products_data[0].m_rdata[PIdx::uy][slot0_idx];
409 auto& uz0 = soa_products_data[0].m_rdata[PIdx::uz][slot0_idx];
410 auto& ux2 = soa_products_data[2].m_rdata[PIdx::ux][slot2_idx];
411 auto& uy2 = soa_products_data[2].m_rdata[PIdx::uy][slot2_idx];
412 auto& uz2 = soa_products_data[2].m_rdata[PIdx::uz][slot2_idx];
413 auto& ux3 = soa_products_data[3].m_rdata[PIdx::ux][slot3_idx];
414 auto& uy3 = soa_products_data[3].m_rdata[PIdx::uy][slot3_idx];
415 auto& uz3 = soa_products_data[3].m_rdata[PIdx::uz][slot3_idx];
416
417#if defined(WARPX_DIM_RZ) || defined(WARPX_DIM_RCYLINDER)
418 /* In RZ and RCYLINDER geometry, macroparticles can collide with other macroparticles
419 * in the same *cylindrical* cell. For this reason, collisions between macroparticles
420 * are actually not local in space. In this case, the underlying assumption is that
421 * particles within the same cylindrical cell represent a cylindrically-symmetry
422 * momentum distribution function. Therefore, here, we temporarily rotate the
423 * momentum of one of the macroparticles in agreement with this cylindrical symmetry.
424 * (This is technically only valid if we use only the m=0 azimuthal mode in the simulation;
425 * there is a corresponding assert statement at initialization.)
426 */
427 amrex::ParticleReal const theta = (
428 soa_products_data[2].m_rdata[PIdx::theta][slot2_idx]
429 - soa_products_data[0].m_rdata[PIdx::theta][slot0_idx]
430 );
431 amrex::ParticleReal const ux0buf = ux0;
432 ux0 = ux0buf*std::cos(theta) - uy0*std::sin(theta);
433 uy0 = ux0buf*std::sin(theta) + uy0*std::cos(theta);
434#endif
435
436 // for simplicity (for now) we assume non-relativistic particles
437 // and simply calculate the center-of-momentum velocity from the
438 // rest masses
439 auto const uCOM_x = (m0 * ux0 + m1 * ux3) / (m0 + m1);
440 auto const uCOM_y = (m0 * uy0 + m1 * uy3) / (m0 + m1);
441 auto const uCOM_z = (m0 * uz0 + m1 * uz3) / (m0 + m1);
442
443 // transform to COM frame
444 ux0 -= uCOM_x;
445 uy0 -= uCOM_y;
446 uz0 -= uCOM_z;
447 ux2 -= uCOM_x;
448 uy2 -= uCOM_y;
449 uz2 -= uCOM_z;
450 ux3 -= uCOM_x;
451 uy3 -= uCOM_y;
452 uz3 -= uCOM_z;
453
454 // calculate kinetic energy of the collision (in eV)
455 const amrex::ParticleReal E1 = (
456 0.5_prt * m0 * (ux0*ux0 + uy0*uy0 + uz0*uz0) / PhysConst::q_e
457 );
458 const amrex::ParticleReal E2 = (
459 0.5_prt * m1 * (ux3*ux3 + uy3*uy3 + uz3*uz3) / PhysConst::q_e
460 );
461 const amrex::ParticleReal E_coll = E1 + E2;
462
463 // subtract the energy cost for ionization
464 const amrex::ParticleReal E_out = (E_coll - reaction_energy) * PhysConst::q_e;
465
466 // Momentum and energy division after the ionization event
467 // is done as follows:
468 // Three numbers are generated that satisfy the triangle
469 // inequality. These numbers will be scaled to give the
470 // momentum for each particle (satisfying the triangle
471 // inequality ensures that the momentum vectors can be
472 // arranged such that the net linear momentum is zero).
473 // Product 2 is definitely an ion, so we first choose its
474 // proportion to be n_2 = min(E2 / E_coll, 0.5 * R), where
475 // R is a random number between 0 and 1.
476 // The other two numbers (n_0 & n_1) are then found
477 // by choosing a random point on the ellipse characterized
478 // by the semi-major and semi-minor axes
479 // a = (1 - n_0) / 2.0
480 // b = 0.5 * sqrt(1 - 2 * n_0)
481 // The numbers are found by randomly sampling an x value
482 // between -a and a, and finding the corresponding y value
483 // that falls on the ellipse: y^2 = b^2 - b^2/a^2 * x^2.
484 // Then n_0 = sqrt(y^2 + (x - n_2/2)^2) and
485 // n_1 = 1 - n_2 - n_0.
486 // Next, we need to find the number C, such that if
487 // p_i = C * n_i, we would have E_0 + E_1 + E_2 = E_out
488 // where E_i = p_i^2 / (2 M_i), i.e.,
489 // C^2 * \sum_i [n_i^2 / (2 M_i)] = E_out
490 // After C is determined the momentum vectors are arranged
491 // such that the net linear momentum is zero.
492
493 const double n_2 = std::min<double>(E2 / E_coll, 0.5 * amrex::Random(engine));
494
495 // find ellipse semi-major and minor axis
496 const double a = 0.5 * (1.0 - n_2);
497 const double b = 0.5 * std::sqrt(1.0 - 2.0 * n_2);
498
499 // sample random x value and calculate y
500 const double x = (2.0 * amrex::Random(engine) - 1.0) * a;
501 const double y2 = b*b - b*b/(a*a) * x*x;
502 const double n_0 = std::sqrt(y2 + x*x - x*n_2 + 0.25*n_2*n_2);
503 const double n_1 = 1.0 - n_0 - n_2;
504
505 // calculate the value of C
506 const double C = std::sqrt(E_out / (
507 n_0*n_0 / (2.0 * m0) + n_1*n_1 / (2.0 * mass_slot2) + n_2*n_2 / (2.0 * mass_slot3)
508 ));
509
510 // Now that appropriate momenta are set for each outgoing species
511 // the directions for the velocity vectors must be chosen such
512 // that the net linear momentum in the current frame is 0.
513 // This is achieved by arranging the momentum vectors in
514 // a triangle and finding the required angles between the vectors.
515 const double cos_alpha = (n_0*n_0 + n_1*n_1 - n_2*n_2) / (2.0 * n_0 * n_1);
516 const double sin_alpha = std::sqrt(1.0 - cos_alpha*cos_alpha);
517 const double cos_gamma = (n_0*n_0 + n_2*n_2 - n_1*n_1) / (2.0 * n_0 * n_2);
518 const double sin_gamma = std::sqrt(1.0 - cos_gamma*cos_gamma);
519
520 // choose random theta and phi values (orientation of the triangle)
521 const double Theta = amrex::Random(engine) * 2.0 * MathConst::pi;
522 const double phi = amrex::Random(engine) * MathConst::pi;
523
524 const double cos_theta = std::cos(Theta);
525 const double sin_theta = std::sin(Theta);
526 const double cos_phi = std::cos(phi);
527 const double sin_phi = std::sin(phi);
528
529 // calculate the velocity components for each particle
530 ux0 = static_cast<amrex::ParticleReal>(C * n_0 / m0 * cos_theta * cos_phi);
531 uy0 = static_cast<amrex::ParticleReal>(C * n_0 / m0 * cos_theta * sin_phi);
532 uz0 = static_cast<amrex::ParticleReal>(-C * n_0 / m0 * sin_theta);
533
534 ux2 = static_cast<amrex::ParticleReal>(C * n_1 / mass_slot2 * (-cos_alpha * cos_theta * cos_phi - sin_alpha * sin_phi));
535 uy2 = static_cast<amrex::ParticleReal>(C * n_1 / mass_slot2 * (-cos_alpha * cos_theta * sin_phi + sin_alpha * cos_phi));
536 uz2 = static_cast<amrex::ParticleReal>(C * n_1 / mass_slot2 * (cos_alpha * sin_theta));
537
538 ux3 = static_cast<amrex::ParticleReal>(C * n_2 / mass_slot3 * (-cos_gamma * cos_theta * cos_phi + sin_gamma * sin_phi));
539 uy3 = static_cast<amrex::ParticleReal>(C * n_2 / mass_slot3 * (-cos_gamma * cos_theta * sin_phi - sin_gamma * cos_phi));
540 uz3 = static_cast<amrex::ParticleReal>(C * n_2 / mass_slot3 * (cos_gamma * sin_theta));
541
542 // transform back to labframe
543 ux0 += uCOM_x;
544 uy0 += uCOM_y;
545 uz0 += uCOM_z;
546 ux2 += uCOM_x;
547 uy2 += uCOM_y;
548 uz2 += uCOM_z;
549 ux3 += uCOM_x;
550 uy3 += uCOM_y;
551 uz3 += uCOM_z;
552
553#if defined(WARPX_DIM_RZ) || defined(WARPX_DIM_RCYLINDER)
554 /* Undo the earlier velocity rotation. */
555 amrex::ParticleReal const ux0buf_new = ux0;
556 ux0 = ux0buf_new*std::cos(-theta) - uy0*std::sin(-theta);
557 uy0 = ux0buf_new*std::sin(-theta) + uy0*std::cos(-theta);
558#endif
559 }
560 });
561
562 // Initialize the user runtime components
563 for (int i = 0; i < m_num_product_species; i++)
564 {
565 const int start_index = int(products_np[i]);
566 const int stop_index = int(products_np[i] + num_added_vec[i]);
568 *pc_products[i], start_index, stop_index);
569 }
570
572 return num_added_vec;
573 }
574
575private:
576 // How many different type of species the collision produces
578 // If ionization collisions are included, what is the energy cost
580 // Vector of size m_num_product_species: for each product slot, the number of new particles
581 // created per *reaction* event (ionization, two-product reaction, charge exchange).
582 // Weight-split particles added to slots 0 and 1 during non-reaction events are NOT counted
583 // here; they are tracked separately via `no_product_total` inside operator().
586};
587#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
@ ELASTIC
Definition ScatteringProcess.H:22
@ TWOPRODUCT_REACTION
Definition ScatteringProcess.H:24
@ IONIZATION
Definition ScatteringProcess.H:26
@ FORWARD
Definition ScatteringProcess.H:27
@ Forward
Definition WarpXAlgorithmSelection.H:198
Definition MultiParticleContainer.H:69
typename ParticleBins::index_type index_type
Definition SplitAndScatterFunc.H:31
typename ParticleTileType::ParticleTileDataType ParticleTileDataType
Definition SplitAndScatterFunc.H:29
amrex::ParticleReal m_reaction_energy
Definition SplitAndScatterFunc.H:579
int m_num_product_species
Definition SplitAndScatterFunc.H:577
SplitAndScatterFunc()=default
Default constructor of the SplitAndScatterFunc class.
typename WarpXParticleContainer::ParticleType ParticleType
Definition SplitAndScatterFunc.H:27
CollisionType m_collision_type
Definition SplitAndScatterFunc.H:585
amrex::DenseBins< ParticleTileDataType > ParticleBins
Definition SplitAndScatterFunc.H:30
typename WarpXParticleContainer::ParticleTileType ParticleTileType
Definition SplitAndScatterFunc.H:28
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:97
amrex::Gpu::HostVector< int > m_num_products_host
Definition SplitAndScatterFunc.H:584
WarpXParticleContainer::ParticleTileType::ParticleTileDataType SoaData_type
Definition SplitAndScatterFunc.H:32
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
T PrefixSum(N n, FIN const &fin, FOUT const &fout, TYPE, RetSum a_ret_sum=retSum)
PinnedVector< T > HostVector
PODVector< T, ArenaAllocator< T > > DeviceVector
Real Random()
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:111
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:217
constexpr auto q_e
elementary charge [C]
Definition constant.H:162
constexpr auto pi
ratio of a circle's circumference to its diameter
Definition constant.H:29
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
void Abort(const std::string &msg)
const int[]
AMREX_ATTRIBUTE_FLATTEN_FOR void ParallelForRNG(T n, L const &f) noexcept
@ uz
Definition WarpXParticleContainer.H:70
@ w
Definition WarpXParticleContainer.H:70
@ uy
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