WarpX
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 
16 #include "Utils/ParticleUtils.H"
17 
23 {
24  // Define shortcuts for frequently-used type names
31 
32 public:
36  SplitAndScatterFunc () = default;
37 
44  SplitAndScatterFunc (const std::string& collision_name, MultiParticleContainer const * mypc);
45 
54  const index_type& n_total_pairs,
55  ParticleTileType& ptile1, ParticleTileType& ptile2,
56  const amrex::Vector<WarpXParticleContainer*>& pc_products,
57  ParticleTileType** AMREX_RESTRICT tile_products,
58  const amrex::ParticleReal m1, const amrex::ParticleReal m2,
59  const amrex::Vector<amrex::ParticleReal>& /*products_mass*/,
60  const index_type* AMREX_RESTRICT mask,
61  const amrex::Vector<index_type>& products_np,
62  const SmartCopy* AMREX_RESTRICT copy_species1,
63  const SmartCopy* AMREX_RESTRICT copy_species2,
64  const index_type* AMREX_RESTRICT p_pair_indices_1,
65  const index_type* AMREX_RESTRICT p_pair_indices_2,
66  const amrex::ParticleReal* AMREX_RESTRICT p_pair_reaction_weight ) const
67  {
68  using namespace amrex::literals;
69 
70  if (n_total_pairs == 0) { return amrex::Vector<int>(m_num_product_species, 0); }
71 
72  amrex::Gpu::DeviceVector<index_type> offsets(n_total_pairs);
73  index_type* AMREX_RESTRICT offsets_data = offsets.data();
74  const index_type* AMREX_RESTRICT p_offsets = offsets.dataPtr();
75 
76  // The following is used to calculate the appropriate offsets. Note that
77  // a standard cummulative sum is not appropriate since the mask is also
78  // used to specify the type of collision and can therefore have values >1
79  auto const total = amrex::Scan::PrefixSum<index_type>(n_total_pairs,
80  [=] AMREX_GPU_DEVICE (index_type i) -> index_type { return mask[i] ? 1 : 0; },
81  [=] AMREX_GPU_DEVICE (index_type i, index_type s) { offsets_data[i] = s; },
83  );
84 
86  for (int i = 0; i < m_num_product_species; i++)
87  {
88  // How many particles of product species i are created.
89  const index_type num_added = total * m_num_products_host[i];
90  num_added_vec[i] = static_cast<int>(num_added);
91  tile_products[i]->resize(products_np[i] + num_added);
92  }
93 
94  const auto soa_1 = ptile1.getParticleTileData();
95  const auto soa_2 = ptile2.getParticleTileData();
96 
97  amrex::ParticleReal* AMREX_RESTRICT w1 = soa_1.m_rdata[PIdx::w];
98  amrex::ParticleReal* AMREX_RESTRICT w2 = soa_2.m_rdata[PIdx::w];
99  uint64_t* AMREX_RESTRICT idcpu1 = soa_1.m_idcpu;
100  uint64_t* AMREX_RESTRICT idcpu2 = soa_2.m_idcpu;
101 
102  // Create necessary GPU vectors, that will be used in the kernel below
103  amrex::Vector<SoaData_type> soa_products;
104  for (int i = 0; i < m_num_product_species; i++)
105  {
106  soa_products.push_back(tile_products[i]->getParticleTileData());
107  }
108 #ifdef AMREX_USE_GPU
111 
112  amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, soa_products.begin(),
113  soa_products.end(),
114  device_soa_products.begin());
115  amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, products_np.begin(),
116  products_np.end(),
117  device_products_np.begin());
118 
120  SoaData_type* AMREX_RESTRICT soa_products_data = device_soa_products.data();
121  const index_type* AMREX_RESTRICT products_np_data = device_products_np.data();
122 #else
123  SoaData_type* AMREX_RESTRICT soa_products_data = soa_products.data();
124  const index_type* AMREX_RESTRICT products_np_data = products_np.data();
125 #endif
126 
127  const int* AMREX_RESTRICT p_num_products_device = m_num_products_device.data();
128 
129  amrex::ParallelForRNG(n_total_pairs,
130  [=] AMREX_GPU_DEVICE (int i, amrex::RandomEngine const& engine) noexcept
131  {
132  if (mask[i])
133  {
134  // for now we ignore the possibility of having actual reaction
135  // products - only duplicating (splitting) of the colliding
136  // particles is supported.
137 
138  const auto product1_index = products_np_data[0] +
139  (p_offsets[i]*p_num_products_device[0] + 0);
140  // Make a copy of the particle from species 1
141  copy_species1[0](soa_products_data[0], soa_1, static_cast<int>(p_pair_indices_1[i]),
142  static_cast<int>(product1_index), engine);
143  // Set the weight of the new particles to p_pair_reaction_weight[i]
144  soa_products_data[0].m_rdata[PIdx::w][product1_index] = p_pair_reaction_weight[i];
145 
146  const auto product2_index = products_np_data[1] +
147  (p_offsets[i]*p_num_products_device[1] + 0);
148  // Make a copy of the particle from species 2
149  copy_species2[1](soa_products_data[1], soa_2, static_cast<int>(p_pair_indices_2[i]),
150  static_cast<int>(product2_index), engine);
151  // Set the weight of the new particles to p_pair_reaction_weight[i]
152  soa_products_data[1].m_rdata[PIdx::w][product2_index] = p_pair_reaction_weight[i];
153 
154  // Remove p_pair_reaction_weight[i] from the colliding particles' weights
156  w1[p_pair_indices_1[i]], idcpu1[p_pair_indices_1[i]], p_pair_reaction_weight[i]);
158  w2[p_pair_indices_2[i]], idcpu2[p_pair_indices_2[i]], p_pair_reaction_weight[i]);
159 
160  // Set the child particle properties appropriately
161  auto& ux1 = soa_products_data[0].m_rdata[PIdx::ux][product1_index];
162  auto& uy1 = soa_products_data[0].m_rdata[PIdx::uy][product1_index];
163  auto& uz1 = soa_products_data[0].m_rdata[PIdx::uz][product1_index];
164  auto& ux2 = soa_products_data[1].m_rdata[PIdx::ux][product2_index];
165  auto& uy2 = soa_products_data[1].m_rdata[PIdx::uy][product2_index];
166  auto& uz2 = soa_products_data[1].m_rdata[PIdx::uz][product2_index];
167 
168  // for simplicity (for now) we assume non-relativistic particles
169  // and simply calculate the center-of-momentum velocity from the
170  // rest masses
171  auto const uCOM_x = (m1 * ux1 + m2 * ux2) / (m1 + m2);
172  auto const uCOM_y = (m1 * uy1 + m2 * uy2) / (m1 + m2);
173  auto const uCOM_z = (m1 * uz1 + m2 * uz2) / (m1 + m2);
174 
175  // transform to COM frame
176  ux1 -= uCOM_x;
177  uy1 -= uCOM_y;
178  uz1 -= uCOM_z;
179  ux2 -= uCOM_x;
180  uy2 -= uCOM_y;
181  uz2 -= uCOM_z;
182 
183  if (mask[i] == int(ScatteringProcessType::ELASTIC)) {
184  // randomly rotate the velocity vector for the first particle
186  ux1, uy1, uz1, std::sqrt(ux1*ux1 + uy1*uy1 + uz1*uz1), engine
187  );
188  // set the second particles velocity so that the total momentum
189  // is zero
190  ux2 = -ux1 * m1 / m2;
191  uy2 = -uy1 * m1 / m2;
192  uz2 = -uz1 * m1 / m2;
193  } else if (mask[i] == int(ScatteringProcessType::BACK)) {
194  // reverse the velocity vectors of both particles
195  ux1 *= -1.0_prt;
196  uy1 *= -1.0_prt;
197  uz1 *= -1.0_prt;
198  ux2 *= -1.0_prt;
199  uy2 *= -1.0_prt;
200  uz2 *= -1.0_prt;
201  } else if (mask[i] == int(ScatteringProcessType::CHARGE_EXCHANGE)) {
202  if (std::abs(m1 - m2) < 1e-28) {
203  auto const temp_ux = ux1;
204  auto const temp_uy = uy1;
205  auto const temp_uz = uz1;
206  ux1 = ux2;
207  uy1 = uy2;
208  uz1 = uz2;
209  ux2 = temp_ux;
210  uy2 = temp_uy;
211  uz2 = temp_uz;
212  }
213  else {
214  amrex::Abort("Uneven mass charge-exchange not implemented yet.");
215  }
216  }
217  else {
218  amrex::Abort("Unknown scattering process.");
219  }
220  // transform back to labframe
221  ux1 += uCOM_x;
222  uy1 += uCOM_y;
223  uz1 += uCOM_z;
224  ux2 += uCOM_x;
225  uy2 += uCOM_y;
226  uz2 += uCOM_z;
227  }
228  });
229 
230  // Initialize the user runtime components
231  for (int i = 0; i < m_num_product_species; i++)
232  {
233  const int start_index = int(products_np[i]);
234  const int stop_index = int(products_np[i] + num_added_vec[i]);
236  0, 0,
237  pc_products[i]->getUserRealAttribs(), pc_products[i]->getUserIntAttribs(),
238  pc_products[i]->getParticleComps(), pc_products[i]->getParticleiComps(),
239  pc_products[i]->getUserRealAttribParser(),
240  pc_products[i]->getUserIntAttribParser(),
241 #ifdef WARPX_QED
242  false, // do not initialize QED quantities, since they were initialized
243  // when calling the SmartCopy functors
244  pc_products[i]->get_breit_wheeler_engine_ptr(),
245  pc_products[i]->get_quantum_sync_engine_ptr(),
246 #endif
247  pc_products[i]->getIonizationInitialLevel(),
248  start_index, stop_index);
249  }
250 
252  return num_added_vec;
253  }
254 
255 private:
256  // How many different type of species the collision produces
258  // Vectors of size m_num_product_species storing how many particles of a given species are
259  // produced by a collision event. These vectors are duplicated (one version for host and one
260  // for device) which is necessary with GPUs but redundant on CPU.
264 };
265 #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
Definition: MultiParticleContainer.H:66
This class defines an operator to create product particles from DSMC collisions and sets the particle...
Definition: SplitAndScatterFunc.H:23
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, const 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
Function that performs the particle scattering and injection due to binary collisions.
Definition: SplitAndScatterFunc.H:53
typename ParticleBins::index_type index_type
Definition: SplitAndScatterFunc.H:29
typename ParticleTileType::ParticleTileDataType ParticleTileDataType
Definition: SplitAndScatterFunc.H:27
int m_num_product_species
Definition: SplitAndScatterFunc.H:257
SplitAndScatterFunc()=default
Default constructor of the SplitAndScatterFunc class.
typename WarpXParticleContainer::ParticleType ParticleType
Definition: SplitAndScatterFunc.H:25
CollisionType m_collision_type
Definition: SplitAndScatterFunc.H:263
typename WarpXParticleContainer::ParticleTileType ParticleTileType
Definition: SplitAndScatterFunc.H:26
amrex::Gpu::HostVector< int > m_num_products_host
Definition: SplitAndScatterFunc.H:262
amrex::Gpu::DeviceVector< int > m_num_products_device
Definition: SplitAndScatterFunc.H:261
unsigned int index_type
T * data() noexcept
iterator begin() noexcept
T * dataPtr() noexcept
ParticleTile< ParticleType, NArrayReal, NArrayInt, Allocator > ParticleTileType
AMREX_GPU_HOST_DEVICE AMREX_INLINE void remove_weight_from_colliding_particle(amrex::ParticleReal &weight, uint64_t &idcpu, const amrex::ParticleReal reaction_weight)
Subtract given weight from particle and set its ID to invalid if the weight reaches zero.
Definition: BinaryCollisionUtils.H:135
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::map< std::string, int > &particle_comps, const std::map< std::string, int > &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:162
typename ParticleTileType::ParticleTileDataType ParticleTileDataType
Definition: ParticleUtils.cpp:33
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
i
Definition: check_interp_points_and_weights.py:174
@ uz
Definition: NamedComponentParticleContainer.H:34
@ w
weight
Definition: NamedComponentParticleContainer.H:33
@ uy
Definition: NamedComponentParticleContainer.H:34
@ ux
Definition: NamedComponentParticleContainer.H:34
This is a functor for performing a "smart copy" that works in both host and device code.
Definition: SmartCopy.H:34
ParticleTileData< StorageParticleType, NArrayReal, NArrayInt > ParticleTileDataType