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"
18
24{
25 // Define shortcuts for frequently-used type names
28 using ParticleTileDataType = typename ParticleTileType::ParticleTileDataType;
31 using SoaData_type = WarpXParticleContainer::ParticleTileType::ParticleTileDataType;
32
33public:
37 SplitAndScatterFunc () = default;
38
45 SplitAndScatterFunc (const std::string& collision_name, MultiParticleContainer const * mypc);
46
55 const index_type& n_total_pairs,
56 ParticleTileType& ptile1, ParticleTileType& ptile2,
58 ParticleTileType** AMREX_RESTRICT tile_products,
59 const amrex::ParticleReal m1, const amrex::ParticleReal m2,
60 const amrex::Vector<amrex::ParticleReal>& /*products_mass*/,
62 amrex::Vector<index_type>& products_np,
63 const SmartCopy* AMREX_RESTRICT copy_species1,
64 const SmartCopy* AMREX_RESTRICT copy_species2,
65 const index_type* AMREX_RESTRICT p_pair_indices_1,
66 const index_type* AMREX_RESTRICT p_pair_indices_2,
67 const amrex::ParticleReal* AMREX_RESTRICT p_pair_reaction_weight,
68 const amrex::ParticleReal* /*p_product_data*/ ) const
69 {
70 using namespace amrex::literals;
71
72 // Return a vector of zeros, indicating that for all the "product" species
73 // there were no new particles added.
74 if (n_total_pairs == 0) { return amrex::Vector<int>(m_num_product_species, 0); }
75
76 // The following is used to calculate the appropriate offsets for processes
77 // that do not produce macroparticles in new species (i.e., not ionization, not two-product reaction).
78 // Note that a standard cummulative sum is not appropriate since the
79 // mask is also used to specify the type of collision and can therefore
80 // have values >1
81 amrex::Gpu::DeviceVector<index_type> no_product_offsets(n_total_pairs);
82 index_type* AMREX_RESTRICT no_product_offsets_data = no_product_offsets.data();
83 const index_type* AMREX_RESTRICT no_product_p_offsets = no_product_offsets.dataPtr();
84 auto const no_product_total = amrex::Scan::PrefixSum<index_type>(n_total_pairs,
86 return ((mask[i] > 0) &
89 },
90 [=] AMREX_GPU_DEVICE (index_type i, index_type s) { no_product_offsets_data[i] = s; },
92 );
93
95 for (int i = 0; i < 2; i++)
96 {
97 // Record the number of non product producing events lead to new
98 // particles for species1 and 2. Only 1 particle is created for
99 // each species (the piece that breaks off to have equal weight)
100 // particles.
101 num_added_vec[i] = static_cast<int>(no_product_total);
102 }
103
104 // The following is used to calculate the appropriate offsets for
105 // product producing processes (i.e., ionization and two-product reaction).
106 // Note that a standard cummulative sum is not appropriate since the
107 // mask is also used to specify the type of collision and can therefore
108 // have values >1
109 amrex::Gpu::DeviceVector<index_type> with_product_offsets(n_total_pairs);
110 index_type* AMREX_RESTRICT with_product_offsets_data = with_product_offsets.data();
111 const index_type* AMREX_RESTRICT with_product_p_offsets = with_product_offsets.dataPtr();
112 auto const with_product_total = amrex::Scan::PrefixSum<index_type>(n_total_pairs,
114 return ((mask[i] == int(ScatteringProcessType::IONIZATION)) |
116 },
117 [=] AMREX_GPU_DEVICE (index_type i, index_type s) { with_product_offsets_data[i] = s; },
119 );
120
121 for (int i = 0; i < m_num_product_species; i++)
122 {
123 // Add the number of product producing events to the species involved in those processes.
124 int num_products = m_num_products_host[i];
125 const index_type num_added = with_product_total * num_products;
126 num_added_vec[i] += static_cast<int>(num_added);
127 }
128
129 // resize the particle tiles to accomodate the new particles
130 // The code works correctly, even if the same species appears
131 // several times in the product species list.
132 // (e.g., for e- + H -> 2 e- + H+, the product species list is [e-, H, e-, H+])
133 // In that case, the species that appears multiple times is resized several times ;
134 // `products_np` keeps track of array positions at which it has been resized.
135 for (int i = 0; i < m_num_product_species; i++)
136 {
137 products_np[i] = tile_products[i]->numParticles();
138 tile_products[i]->resize(products_np[i] + num_added_vec[i]);
139 }
140
141 const auto soa_1 = ptile1.getParticleTileData();
142 const auto soa_2 = ptile2.getParticleTileData();
143
144 // Create necessary GPU vectors, that will be used in the kernel below
145 amrex::Vector<SoaData_type> soa_products;
146 for (int i = 0; i < m_num_product_species; i++)
147 {
148 soa_products.push_back(tile_products[i]->getParticleTileData());
149 }
150#ifdef AMREX_USE_GPU
153
155 soa_products.end(),
156 device_soa_products.begin());
158 products_np.end(),
159 device_products_np.begin());
160
162 SoaData_type* AMREX_RESTRICT soa_products_data = device_soa_products.data();
163 const index_type* AMREX_RESTRICT products_np_data = device_products_np.data();
164#else
165 SoaData_type* AMREX_RESTRICT soa_products_data = soa_products.data();
166 const index_type* AMREX_RESTRICT products_np_data = products_np.data();
167#endif
168
169 const int num_product_species = m_num_product_species;
170 const auto reaction_energy = m_reaction_energy;
171
172 // Grab the masses of the reaction products
173 amrex::ParticleReal mass_product1 = 0;
174 amrex::ParticleReal mass_product2 = 0;
175 if (num_product_species > 2) {
176 mass_product1 = pc_products[2]->getMass();
177 mass_product2 = pc_products[3]->getMass();
178 }
179
180 // First perform all non-product producing collisions
181 amrex::ParallelForRNG(n_total_pairs,
182 [=] AMREX_GPU_DEVICE (int i, amrex::RandomEngine const& engine) noexcept
183 {
185 {
186 const auto product1_index = products_np_data[0] + no_product_p_offsets[i];
187 // Make a copy of the particle from species 1
188 copy_species1[0](soa_products_data[0], soa_1, static_cast<int>(p_pair_indices_1[i]),
189 static_cast<int>(product1_index), engine);
190 // Set the weight of the new particles to p_pair_reaction_weight[i]
191 soa_products_data[0].m_rdata[PIdx::w][product1_index] = p_pair_reaction_weight[i];
192
193 const auto product2_index = products_np_data[1] + no_product_p_offsets[i];
194 // Make a copy of the particle from species 2
195 copy_species2[1](soa_products_data[1], soa_2, static_cast<int>(p_pair_indices_2[i]),
196 static_cast<int>(product2_index), engine);
197 // Set the weight of the new particles to p_pair_reaction_weight[i]
198 soa_products_data[1].m_rdata[PIdx::w][product2_index] = p_pair_reaction_weight[i];
199
200 // Set the child particle properties appropriately
201 auto& ux1 = soa_products_data[0].m_rdata[PIdx::ux][product1_index];
202 auto& uy1 = soa_products_data[0].m_rdata[PIdx::uy][product1_index];
203 auto& uz1 = soa_products_data[0].m_rdata[PIdx::uz][product1_index];
204 auto& ux2 = soa_products_data[1].m_rdata[PIdx::ux][product2_index];
205 auto& uy2 = soa_products_data[1].m_rdata[PIdx::uy][product2_index];
206 auto& uz2 = soa_products_data[1].m_rdata[PIdx::uz][product2_index];
207
208#if (defined WARPX_DIM_RZ)
209 /* In RZ geometry, macroparticles can collide with other macroparticles
210 * in the same *cylindrical* cell. For this reason, collisions between macroparticles
211 * are actually not local in space. In this case, the underlying assumption is that
212 * particles within the same cylindrical cell represent a cylindrically-symmetry
213 * momentum distribution function. Therefore, here, we temporarily rotate the
214 * momentum of one of the macroparticles in agreement with this cylindrical symmetry.
215 * (This is technically only valid if we use only the m=0 azimuthal mode in the simulation;
216 * there is a corresponding assert statement at initialization.)
217 */
218 amrex::ParticleReal const theta = (
219 soa_products_data[1].m_rdata[PIdx::theta][product2_index]
220 - soa_products_data[0].m_rdata[PIdx::theta][product1_index]
221 );
222 amrex::ParticleReal const ux1buf = ux1;
223 ux1 = ux1buf*std::cos(theta) - uy1*std::sin(theta);
224 uy1 = ux1buf*std::sin(theta) + uy1*std::cos(theta);
225#endif
226
227 // for simplicity (for now) we assume non-relativistic particles
228 // and simply calculate the center-of-momentum velocity from the
229 // rest masses
230 // TODO: this could be made relativistic by using TwoProductComputeProductMomenta
231 auto const uCOM_x = (m1 * ux1 + m2 * ux2) / (m1 + m2);
232 auto const uCOM_y = (m1 * uy1 + m2 * uy2) / (m1 + m2);
233 auto const uCOM_z = (m1 * uz1 + m2 * uz2) / (m1 + m2);
234
235 // transform to COM frame
236 ux1 -= uCOM_x;
237 uy1 -= uCOM_y;
238 uz1 -= uCOM_z;
239 ux2 -= uCOM_x;
240 uy2 -= uCOM_y;
241 uz2 -= uCOM_z;
242
243 if (mask[i] == int(ScatteringProcessType::ELASTIC)) {
244 // randomly rotate the velocity vector for the first particle
246 ux1, uy1, uz1, std::sqrt(ux1*ux1 + uy1*uy1 + uz1*uz1), engine
247 );
248 // set the second particles velocity so that the total momentum
249 // is zero
250 ux2 = -ux1 * m1 / m2;
251 uy2 = -uy1 * m1 / m2;
252 uz2 = -uz1 * m1 / m2;
253 } else if (mask[i] == int(ScatteringProcessType::BACK)) {
254 // reverse the velocity vectors of both particles
255 ux1 *= -1.0_prt;
256 uy1 *= -1.0_prt;
257 uz1 *= -1.0_prt;
258 ux2 *= -1.0_prt;
259 uy2 *= -1.0_prt;
260 uz2 *= -1.0_prt;
261 } else if (mask[i] == int(ScatteringProcessType::FORWARD)) {
262 amrex::Abort("Forward scattering with DSMC not implemented yet.");
263 }
264 else {
265 amrex::Abort("Unknown scattering process.");
266 }
267 // transform back to labframe
268 ux1 += uCOM_x;
269 uy1 += uCOM_y;
270 uz1 += uCOM_z;
271 ux2 += uCOM_x;
272 uy2 += uCOM_y;
273 uz2 += uCOM_z;
274
275#if (defined WARPX_DIM_RZ)
276 /* Undo the earlier velocity rotation. */
277 amrex::ParticleReal const ux1buf_new = ux1;
278 ux1 = ux1buf_new*std::cos(-theta) - uy1*std::sin(-theta);
279 uy1 = ux1buf_new*std::sin(-theta) + uy1*std::cos(-theta);
280#endif
281 }
282
283 // Next perform all product-producing collisions
285 {
286 // create a copy of the first product species at the location of species 1
287 const auto reactant1_index = static_cast<int>(p_pair_indices_1[i]);
288 const auto product1_index = products_np_data[2] + with_product_p_offsets[i];
289 copy_species1[2](soa_products_data[2], soa_1, reactant1_index,
290 static_cast<int>(product1_index), engine);
291 // Set the weight of the new particle to p_pair_reaction_weight[i]
292 soa_products_data[2].m_rdata[PIdx::w][product1_index] = p_pair_reaction_weight[i];
293
294 // create a copy of the other product species at the location of species 2
295 const auto reactant2_index = static_cast<int>(p_pair_indices_2[i]);
296 const auto product2_index = products_np_data[3] + with_product_p_offsets[i];
297 copy_species2[3](soa_products_data[3], soa_2, reactant2_index,
298 static_cast<int>(product2_index), engine);
299 // Set the weight of the new particle to p_pair_reaction_weight[i]
300 soa_products_data[3].m_rdata[PIdx::w][product2_index] = p_pair_reaction_weight[i];
301
302 // Update the momenta of the products
303 TwoProductComputeProductMomenta(
304 soa_1.m_rdata[PIdx::ux][reactant1_index],
305 soa_1.m_rdata[PIdx::uy][reactant1_index],
306 soa_1.m_rdata[PIdx::uz][reactant1_index], m1,
307 soa_2.m_rdata[PIdx::ux][reactant2_index],
308 soa_2.m_rdata[PIdx::uy][reactant2_index],
309 soa_2.m_rdata[PIdx::uz][reactant2_index], m2,
310 soa_products_data[2].m_rdata[PIdx::ux][product1_index],
311 soa_products_data[2].m_rdata[PIdx::uy][product1_index],
312 soa_products_data[2].m_rdata[PIdx::uz][product1_index], mass_product1,
313 soa_products_data[3].m_rdata[PIdx::ux][product2_index],
314 soa_products_data[3].m_rdata[PIdx::uy][product2_index],
315 soa_products_data[3].m_rdata[PIdx::uz][product2_index], mass_product2,
316 -reaction_energy*PhysConst::q_e,
317 // TwoProductComputeProductMomenta expects the *released* energy here, hence the negative sign
318 // We also convert from eV to Joules
319 false,
320 // no angular scattering: the products' momenta have the same direction
321 // as that of the incident particle, in the center-of-mass frame
322 engine);
323 }
324 else if (mask[i] == int(ScatteringProcessType::IONIZATION))
325 {
326 const auto species1_index = products_np_data[0] + no_product_total + with_product_p_offsets[i];
327 // Make a copy of the particle from species 1
328 copy_species1[0](soa_products_data[0], soa_1, static_cast<int>(p_pair_indices_1[i]),
329 static_cast<int>(species1_index), engine);
330 // Set the weight of the new particles to p_pair_reaction_weight[i]
331 soa_products_data[0].m_rdata[PIdx::w][species1_index] = p_pair_reaction_weight[i];
332
333 // create a copy of the first product species at the location of species 2
334 // (species2 is the "target species", i.e. the species that is ionized)
335 const auto product1_index = products_np_data[2] + with_product_p_offsets[i];
336 copy_species2[2](soa_products_data[2], soa_2, static_cast<int>(p_pair_indices_2[i]),
337 static_cast<int>(product1_index), engine);
338 // Set the weight of the new particle to p_pair_reaction_weight[i]
339 soa_products_data[2].m_rdata[PIdx::w][product1_index] = p_pair_reaction_weight[i];
340
341 // create a copy of the other product species at the location of species 2
342 // (species2 is the "target species", i.e. the species that is ionized)
343 const auto product2_index = products_np_data[3] + with_product_p_offsets[i];
344 copy_species2[3](soa_products_data[3], soa_2, static_cast<int>(p_pair_indices_2[i]),
345 static_cast<int>(product2_index), engine);
346 // Set the weight of the new particle to p_pair_reaction_weight[i]
347 soa_products_data[3].m_rdata[PIdx::w][product2_index] = p_pair_reaction_weight[i];
348
349 // Grab the colliding particle velocities to calculate the COM
350 // Note that the two product particles currently have the same
351 // velocity as the "target" particle
352 auto& ux1 = soa_products_data[0].m_rdata[PIdx::ux][species1_index];
353 auto& uy1 = soa_products_data[0].m_rdata[PIdx::uy][species1_index];
354 auto& uz1 = soa_products_data[0].m_rdata[PIdx::uz][species1_index];
355 auto& ux_p1 = soa_products_data[2].m_rdata[PIdx::ux][product1_index];
356 auto& uy_p1 = soa_products_data[2].m_rdata[PIdx::uy][product1_index];
357 auto& uz_p1 = soa_products_data[2].m_rdata[PIdx::uz][product1_index];
358 auto& ux_p2 = soa_products_data[3].m_rdata[PIdx::ux][product2_index];
359 auto& uy_p2 = soa_products_data[3].m_rdata[PIdx::uy][product2_index];
360 auto& uz_p2 = soa_products_data[3].m_rdata[PIdx::uz][product2_index];
361
362#if (defined WARPX_DIM_RZ)
363 /* In RZ geometry, macroparticles can collide with other macroparticles
364 * in the same *cylindrical* cell. For this reason, collisions between macroparticles
365 * are actually not local in space. In this case, the underlying assumption is that
366 * particles within the same cylindrical cell represent a cylindrically-symmetry
367 * momentum distribution function. Therefore, here, we temporarily rotate the
368 * momentum of one of the macroparticles in agreement with this cylindrical symmetry.
369 * (This is technically only valid if we use only the m=0 azimuthal mode in the simulation;
370 * there is a corresponding assert statement at initialization.)
371 */
372 amrex::ParticleReal const theta = (
373 soa_products_data[2].m_rdata[PIdx::theta][product1_index]
374 - soa_products_data[0].m_rdata[PIdx::theta][species1_index]
375 );
376 amrex::ParticleReal const ux1buf = ux1;
377 ux1 = ux1buf*std::cos(theta) - uy1*std::sin(theta);
378 uy1 = ux1buf*std::sin(theta) + uy1*std::cos(theta);
379#endif
380
381 // for simplicity (for now) we assume non-relativistic particles
382 // and simply calculate the center-of-momentum velocity from the
383 // rest masses
384 auto const uCOM_x = (m1 * ux1 + m2 * ux_p2) / (m1 + m2);
385 auto const uCOM_y = (m1 * uy1 + m2 * uy_p2) / (m1 + m2);
386 auto const uCOM_z = (m1 * uz1 + m2 * uz_p2) / (m1 + m2);
387
388 // transform to COM frame
389 ux1 -= uCOM_x;
390 uy1 -= uCOM_y;
391 uz1 -= uCOM_z;
392 ux_p1 -= uCOM_x;
393 uy_p1 -= uCOM_y;
394 uz_p1 -= uCOM_z;
395 ux_p2 -= uCOM_x;
396 uy_p2 -= uCOM_y;
397 uz_p2 -= uCOM_z;
398
399 // calculate kinetic energy of the collision (in eV)
400 const amrex::ParticleReal E1 = (
401 0.5_prt * m1 * (ux1*ux1 + uy1*uy1 + uz1*uz1) / PhysConst::q_e
402 );
403 const amrex::ParticleReal E2 = (
404 0.5_prt * m2 * (ux_p2*ux_p2 + uy_p2*uy_p2 + uz_p2*uz_p2) / PhysConst::q_e
405 );
406 const amrex::ParticleReal E_coll = E1 + E2;
407
408 // subtract the energy cost for ionization
409 const amrex::ParticleReal E_out = (E_coll - reaction_energy) * PhysConst::q_e;
410
411 // Momentum and energy division after the ionization event
412 // is done as follows:
413 // Three numbers are generated that satisfy the triangle
414 // inequality. These numbers will be scaled to give the
415 // momentum for each particle (satisfying the triangle
416 // inequality ensures that the momentum vectors can be
417 // arranged such that the net linear momentum is zero).
418 // Product 2 is definitely an ion, so we first choose its
419 // proportion to be n_2 = min(E2 / E_coll, 0.5 * R), where
420 // R is a random number between 0 and 1.
421 // The other two numbers (n_0 & n_1) are then found
422 // by choosing a random point on the ellipse characterized
423 // by the semi-major and semi-minor axes
424 // a = (1 - n_0) / 2.0
425 // b = 0.5 * sqrt(1 - 2 * n_0)
426 // The numbers are found by randomly sampling an x value
427 // between -a and a, and finding the corresponding y value
428 // that falls on the ellipse: y^2 = b^2 - b^2/a^2 * x^2.
429 // Then n_0 = sqrt(y^2 + (x - n_2/2)^2) and
430 // n_1 = 1 - n_2 - n_0.
431 // Next, we need to find the number C, such that if
432 // p_i = C * n_i, we would have E_0 + E_1 + E_2 = E_out
433 // where E_i = p_i^2 / (2 M_i), i.e.,
434 // C^2 * \sum_i [n_i^2 / (2 M_i)] = E_out
435 // After C is determined the momentum vectors are arranged
436 // such that the net linear momentum is zero.
437
438 const double n_2 = std::min<double>(E2 / E_coll, 0.5 * amrex::Random(engine));
439
440 // find ellipse semi-major and minor axis
441 const double a = 0.5 * (1.0 - n_2);
442 const double b = 0.5 * std::sqrt(1.0 - 2.0 * n_2);
443
444 // sample random x value and calculate y
445 const double x = (2.0 * amrex::Random(engine) - 1.0) * a;
446 const double y2 = b*b - b*b/(a*a) * x*x;
447 const double n_0 = std::sqrt(y2 + x*x - x*n_2 + 0.25*n_2*n_2);
448 const double n_1 = 1.0 - n_0 - n_2;
449
450 // calculate the value of C
451 const double C = std::sqrt(E_out / (
452 n_0*n_0 / (2.0 * m1) + n_1*n_1 / (2.0 * mass_product1) + n_2*n_2 / (2.0 * mass_product2)
453 ));
454
455 // Now that appropriate momenta are set for each outgoing species
456 // the directions for the velocity vectors must be chosen such
457 // that the net linear momentum in the current frame is 0.
458 // This is achieved by arranging the momentum vectors in
459 // a triangle and finding the required angles between the vectors.
460 const double cos_alpha = (n_0*n_0 + n_1*n_1 - n_2*n_2) / (2.0 * n_0 * n_1);
461 const double sin_alpha = std::sqrt(1.0 - cos_alpha*cos_alpha);
462 const double cos_gamma = (n_0*n_0 + n_2*n_2 - n_1*n_1) / (2.0 * n_0 * n_2);
463 const double sin_gamma = std::sqrt(1.0 - cos_gamma*cos_gamma);
464
465 // choose random theta and phi values (orientation of the triangle)
466 const double Theta = amrex::Random(engine) * 2.0 * MathConst::pi;
467 const double phi = amrex::Random(engine) * MathConst::pi;
468
469 const double cos_theta = std::cos(Theta);
470 const double sin_theta = std::sin(Theta);
471 const double cos_phi = std::cos(phi);
472 const double sin_phi = std::sin(phi);
473
474 // calculate the velocity components for each particle
475 ux1 = static_cast<amrex::ParticleReal>(C * n_0 / m1 * cos_theta * cos_phi);
476 uy1 = static_cast<amrex::ParticleReal>(C * n_0 / m1 * cos_theta * sin_phi);
477 uz1 = static_cast<amrex::ParticleReal>(-C * n_0 / m1 * sin_theta);
478
479 ux_p1 = static_cast<amrex::ParticleReal>(C * n_1 / mass_product1 * (-cos_alpha * cos_theta * cos_phi - sin_alpha * sin_phi));
480 uy_p1 = static_cast<amrex::ParticleReal>(C * n_1 / mass_product1 * (-cos_alpha * cos_theta * sin_phi + sin_alpha * cos_phi));
481 uz_p1 = static_cast<amrex::ParticleReal>(C * n_1 / mass_product1 * (cos_alpha * sin_theta));
482
483 ux_p2 = static_cast<amrex::ParticleReal>(C * n_2 / mass_product2 * (-cos_gamma * cos_theta * cos_phi + sin_gamma * sin_phi));
484 uy_p2 = static_cast<amrex::ParticleReal>(C * n_2 / mass_product2 * (-cos_gamma * cos_theta * sin_phi - sin_gamma * cos_phi));
485 uz_p2 = static_cast<amrex::ParticleReal>(C * n_2 / mass_product2 * (cos_gamma * sin_theta));
486
487 // transform back to labframe
488 ux1 += uCOM_x;
489 uy1 += uCOM_y;
490 uz1 += uCOM_z;
491 ux_p1 += uCOM_x;
492 uy_p1 += uCOM_y;
493 uz_p1 += uCOM_z;
494 ux_p2 += uCOM_x;
495 uy_p2 += uCOM_y;
496 uz_p2 += uCOM_z;
497
498#if (defined WARPX_DIM_RZ)
499 /* Undo the earlier velocity rotation. */
500 amrex::ParticleReal const ux1buf_new = ux1;
501 ux1 = ux1buf_new*std::cos(-theta) - uy1*std::sin(-theta);
502 uy1 = ux1buf_new*std::sin(-theta) + uy1*std::cos(-theta);
503#endif
504 }
505 });
506
507 // Initialize the user runtime components
508 for (int i = 0; i < m_num_product_species; i++)
509 {
510 const int start_index = int(products_np[i]);
511 const int stop_index = int(products_np[i] + num_added_vec[i]);
513 0, 0,
514 pc_products[i]->getUserRealAttribs(), pc_products[i]->getUserIntAttribs(),
515 pc_products[i]->GetRealSoANames(), pc_products[i]->GetIntSoANames(),
516 pc_products[i]->getUserRealAttribParser(),
517 pc_products[i]->getUserIntAttribParser(),
518#ifdef WARPX_QED
519 false, // do not initialize QED quantities, since they were initialized
520 // when calling the SmartCopy functors
521 pc_products[i]->get_breit_wheeler_engine_ptr(),
522 pc_products[i]->get_quantum_sync_engine_ptr(),
523#endif
524 pc_products[i]->getIonizationInitialLevel(),
525 start_index, stop_index);
526 }
527
529 return num_added_vec;
530 }
531
532private:
533 // How many different type of species the collision produces
535 // If ionization collisions are included, what is the energy cost
536 amrex::ParticleReal m_reaction_energy = 0.0;
537 // Vectors of size m_num_product_species storing how many particles of a given species are
538 // produced by a collision event.
541};
542#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
Definition MultiParticleContainer.H:68
typename ParticleBins::index_type index_type
Definition SplitAndScatterFunc.H:30
typename ParticleTileType::ParticleTileDataType ParticleTileDataType
Definition SplitAndScatterFunc.H:28
amrex::ParticleReal m_reaction_energy
Definition SplitAndScatterFunc.H:536
int m_num_product_species
Definition SplitAndScatterFunc.H:534
SplitAndScatterFunc()=default
Default constructor of the SplitAndScatterFunc class.
typename WarpXParticleContainer::ParticleType ParticleType
Definition SplitAndScatterFunc.H:26
CollisionType m_collision_type
Definition SplitAndScatterFunc.H:540
amrex::DenseBins< ParticleTileDataType > ParticleBins
Definition SplitAndScatterFunc.H:29
AMREX_INLINE amrex::Vector< int > operator()(const index_type &n_total_pairs, ParticleTileType &ptile1, ParticleTileType &ptile2, const amrex::Vector< WarpXParticleContainer * > &pc_products, ParticleTileType **AMREX_RESTRICT tile_products, const amrex::ParticleReal m1, const amrex::ParticleReal m2, const amrex::Vector< amrex::ParticleReal > &, const index_type *AMREX_RESTRICT mask, amrex::Vector< index_type > &products_np, const SmartCopy *AMREX_RESTRICT copy_species1, const SmartCopy *AMREX_RESTRICT copy_species2, const index_type *AMREX_RESTRICT p_pair_indices_1, const index_type *AMREX_RESTRICT p_pair_indices_2, const amrex::ParticleReal *AMREX_RESTRICT p_pair_reaction_weight, const amrex::ParticleReal *) const
Function that performs the particle scattering and injection due to binary collisions.
Definition SplitAndScatterFunc.H:54
typename WarpXParticleContainer::ParticleTileType ParticleTileType
Definition SplitAndScatterFunc.H:27
amrex::Gpu::HostVector< int > m_num_products_host
Definition SplitAndScatterFunc.H:539
WarpXParticleContainer::ParticleTileType::ParticleTileDataType SoaData_type
Definition SplitAndScatterFunc.H:31
iterator begin() noexcept
T * dataPtr() noexcept
T * data() noexcept
void DefaultInitializeRuntimeAttributes(PTile &ptile, const int n_external_attr_real, const int n_external_attr_int, const std::vector< std::string > &user_real_attribs, const std::vector< std::string > &user_int_attribs, const std::vector< std::string > &particle_comps, const std::vector< std::string > &particle_icomps, const std::vector< amrex::Parser * > &user_real_attrib_parser, const std::vector< amrex::Parser * > &user_int_attrib_parser, const bool do_qed_comps, BreitWheelerEngine *p_bw_engine, QuantumSynchrotronEngine *p_qs_engine, const int ionization_initial_level, int start, int stop)
Default initialize runtime attributes in a tile. This routine does not initialize the first n_externa...
Definition DefaultInitialization.H:118
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
constexpr auto q_e
elementary charge [C]
Definition constant.H:158
constexpr auto pi
ratio of a circle's circumference to its diameter
Definition constant.H:29
void synchronize() noexcept
PinnedVector< T > HostVector
void copyAsync(HostToDevice, InIter begin, InIter end, OutIter result) noexcept
static constexpr HostToDevice hostToDevice
void streamSynchronize() noexcept
PODVector< T, ArenaAllocator< T > > DeviceVector
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)
Real Random()
void Abort(const std::string &msg)
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