WarpX
Loading...
Searching...
No Matches
ParticleUtils.H
Go to the documentation of this file.
1/* Copyright 2019-2020 Neil Zaim, Yinjian Zhao
2 *
3 * This file is part of WarpX.
4 *
5 * License: BSD-3-Clause-LBNL
6 */
7#ifndef WARPX_PARTICLE_UTILS_H_
8#define WARPX_PARTICLE_UTILS_H_
9
11#include "Utils/WarpXConst.H"
12
13#include <AMReX_DenseBins.H>
14#include <AMReX_Geometry.H>
15#include <AMReX_Particles.H>
16
17#include <AMReX_BaseFwd.H>
18
19namespace ParticleUtils {
20
21 // Define shortcuts for frequently-used type names
23 using ParticleTileDataType = typename ParticleTileType::ParticleTileDataType;
24
38 amrex::MFIter const & mfi,
40
53 void getCollisionEnergy ( amrex::ParticleReal const u2, double const m,
54 double const M, double& gamma, double& energy )
55 {
56 using std::sqrt;
57 using namespace amrex::literals;
58
59 constexpr auto c2 = PhysConst::c * PhysConst::c;
60
61 gamma = sqrt(1.0_rt + u2 / c2);
62 energy = (
63 2.0_rt * m * M * u2 / (gamma + 1.0_rt)
64 / (M + m + sqrt(m*m + M*M + 2.0_rt * m * M * gamma))
66 }
67
78 void doLorentzTransform ( amrex::ParticleReal& ux, amrex::ParticleReal& uy,
79 amrex::ParticleReal& uz,
80 amrex::ParticleReal const Vx, amrex::ParticleReal const Vy,
81 amrex::ParticleReal const Vz )
82 {
83 using namespace amrex::literals;
84
85 // precompute repeatedly used quantities
86 constexpr auto c2 = PhysConst::c * PhysConst::c;
87 const auto V2 = (Vx*Vx + Vy*Vy + Vz*Vz);
88 const auto gamma_V = 1.0_prt / std::sqrt(1.0_prt - V2 / c2);
89 const auto gamma_u = std::sqrt(1.0_prt + (ux*ux + uy*uy + uz*uz) / c2);
90
91 // copy velocity vector values
92 const auto vx = ux;
93 const auto vy = uy;
94 const auto vz = uz;
95
96 ux = vx * (1.0_prt + (gamma_V - 1.0_prt) * Vx*Vx/V2)
97 + vy * (gamma_V - 1.0_prt) * Vx*Vy/V2
98 + vz * (gamma_V - 1.0_prt) * Vx*Vz/V2
99 - gamma_V * Vx * gamma_u;
100
101 uy = vy * (1.0_prt + (gamma_V - 1.0_prt) * Vy*Vy/V2)
102 + vx * (gamma_V - 1.0_prt) * Vx*Vy/V2
103 + vz * (gamma_V - 1.0_prt) * Vy*Vz/V2
104 - gamma_V * Vy * gamma_u;
105
106 uz = vz * (1.0_prt + (gamma_V - 1.0_prt) * Vz*Vz/V2)
107 + vx * (gamma_V - 1.0_prt) * Vx*Vz/V2
108 + vy * (gamma_V - 1.0_prt) * Vy*Vz/V2
109 - gamma_V * Vz * gamma_u;
110 }
111
122 void doLorentzTransformWithU ( amrex::ParticleReal& ux, amrex::ParticleReal& uy,
123 amrex::ParticleReal& uz,
124 amrex::ParticleReal const Ux, amrex::ParticleReal const Uy,
125 amrex::ParticleReal const Uz )
126 {
127 using namespace amrex::literals;
128
129 constexpr auto c2 = PhysConst::c * PhysConst::c;
130
131 amrex::ParticleReal const Usq = Ux*Ux + Uy*Uy + Uz*Uz;
132 amrex::ParticleReal const U = std::sqrt(Usq);
133 if (U > 0._prt) {
134 amrex::ParticleReal const gamma = std::sqrt(1._prt + Usq/c2);
135 // A nice way of calculating (gamma - 1) when it is small
136 amrex::ParticleReal const gammam1 = Usq/c2/(gamma + 1.0_prt);
137 amrex::ParticleReal const Ux_hat = Ux/U;
138 amrex::ParticleReal const Uy_hat = Uy/U;
139 amrex::ParticleReal const Uz_hat = Uz/U;
140 amrex::ParticleReal const P0 = std::sqrt(1._prt + (ux*ux + uy*uy + uz*uz)/c2);
141 amrex::ParticleReal const udotn = ux*Ux_hat + uy*Uy_hat + uz*Uz_hat;
142 ux = ux + gammam1*udotn*Ux_hat - Ux*P0;
143 uy = uy + gammam1*udotn*Uy_hat - Uy*P0;
144 uz = uz + gammam1*udotn*Uz_hat - Uz*P0;
145 }
146
147 }
148
159 void doLorentzTransformWithP ( amrex::ParticleReal& px, amrex::ParticleReal& py,
160 amrex::ParticleReal& pz, amrex::ParticleReal mass,
161 amrex::ParticleReal const Ux, amrex::ParticleReal const Uy,
162 amrex::ParticleReal const Uz )
163 {
164 using namespace amrex::literals;
165
166 constexpr auto c2 = PhysConst::c * PhysConst::c;
167
168 amrex::ParticleReal const Usq = Ux*Ux + Uy*Uy + Uz*Uz;
169 amrex::ParticleReal const U = std::sqrt(Usq);
170 if (U > 0._prt) {
171 amrex::ParticleReal const gamma = std::sqrt(1._prt + Usq/c2);
172 // A nice way of calculating (gamma - 1) when it is small
173 amrex::ParticleReal const gammam1 = Usq/c2/(gamma + 1.0_prt);
174 amrex::ParticleReal const Ux_hat = Ux/U;
175 amrex::ParticleReal const Uy_hat = Uy/U;
176 amrex::ParticleReal const Uz_hat = Uz/U;
177 amrex::ParticleReal const P0 = std::sqrt(px*px + py*py + pz*pz + mass*mass*c2);
178 amrex::ParticleReal const pdotn = px*Ux_hat + py*Uy_hat + pz*Uz_hat;
179 px = px + gammam1*pdotn*Ux_hat - Ux*P0/PhysConst::c;
180 py = py + gammam1*pdotn*Uy_hat - Uy*P0/PhysConst::c;
181 pz = pz + gammam1*pdotn*Uz_hat - Uz*P0/PhysConst::c;
182 }
183
184 }
185
196 void getRandomVector ( amrex::ParticleReal& x, amrex::ParticleReal& y,
197 amrex::ParticleReal& z, amrex::RandomEngine const& engine )
198 {
199 using std::sqrt;
200 using std::cos;
201 using std::sin;
202 using namespace amrex::literals;
203
204 auto const theta = amrex::Random(engine) * 2.0_prt * MathConst::pi;
205 z = 2.0_prt * amrex::Random(engine) - 1.0_prt;
206 auto const xy = sqrt(1_prt - z*z);
207 x = xy * cos(theta);
208 y = xy * sin(theta);
209 }
210
211
221 void RandomizeVelocity ( amrex::ParticleReal& ux, amrex::ParticleReal& uy,
222 amrex::ParticleReal& uz,
223 const amrex::ParticleReal vp,
224 amrex::RandomEngine const& engine )
225 {
226 amrex::ParticleReal x, y, z;
227 // generate random unit vector for the new velocity direction
228 getRandomVector(x, y, z, engine);
229
230 // scale new vector to have the desired magnitude
231 ux = x * vp;
232 uy = y * vp;
233 uz = z * vp;
234 }
235
236 /* \brief Determines whether the point is within the tilebox, inclusive of the boundaries.
237 * Note that this routine is needed since tilebox.contains excludes the boundaries.
238 * \param[in] tilebox The tilebox being checked
239 * \param[in] point The point being checked
240 * \result true if the point with within the boundary, otherwise false
241 */
243 bool containsInclusive (amrex::RealBox const& tilebox, amrex::XDim3 const point) {
244 const auto *const xlo = tilebox.lo();
245 const auto *const xhi = tilebox.hi();
246 return AMREX_D_TERM((xlo[0] <= point.x) && (point.x <= xhi[0]),
247 && (xlo[1] <= point.y) && (point.y <= xhi[1]),
248 && (xlo[2] <= point.z) && (point.z <= xhi[2]));
249 }
250
251
252 /* \brief Add or subtract a small percent of energy from a
253 * pair of particles such that momentum is not disturbed.
254 *
255 * This is done by treating it as an inelastic scattering event
256 * with potential U = deltaE and zero scattering angle
257 * deltaE > 0 ==> need to take energy away
258 * deltaE < 0 ==> need to add energy
259 *
260 * \param[in/out] uxp, uyp, uzp momenta of the particles
261 * \param[in] w weight of the particles
262 * \param[in] indices indices of particles, listed in cell order
263 * \param[in] cell_start start index of particles in the cell
264 * \param[in] cell_stop start index of particles in the next cell
265 * \param[in] mass mass of the particles
266 * \param[in] energy_fraction fraction of pair's relative energy to change
267 * \param[in] energy_fraction_max max fracton of total relative energy that can be changed
268 * \param[in/out] deltaE amount of energy to be distributed in this species
269 */
270 template <typename T_index>
272 bool ModifyEnergyPairwise (amrex::ParticleReal * const AMREX_RESTRICT uxp,
273 amrex::ParticleReal * const AMREX_RESTRICT uyp,
274 amrex::ParticleReal * const AMREX_RESTRICT uzp,
275 amrex::ParticleReal * const AMREX_RESTRICT w,
276 T_index const * const AMREX_RESTRICT indices,
277 T_index const cell_start,
278 T_index const cell_stop,
279 amrex::ParticleReal const mass,
280 amrex::ParticleReal const energy_fraction,
281 amrex::ParticleReal const energy_fraction_max,
282 amrex::ParticleReal & deltaE )
283 {
284 using namespace amrex::literals;
285
286 constexpr auto c2 = PhysConst::c * PhysConst::c;
287
288 int const numCell = (cell_stop - cell_start);
289
290 // Adjust particle's momentum to absorb deltaE.
291 // This loops over the particles, two at a time, distributing
292 // the energy until all of it has been distributed or failing if not.
293 // If deltaE < 0, it should never fail since energy can always be added
294 // to the particles.
295 // if deltaE > 0, it will sometimes fail because energy cannot always be
296 // removed from the particles without changing the momentum.
297 // The loop is setup to switch between even first and odd first pairs
298 // each loop over the particles.
299 int loop_count = 0;
300 int const max_loop_count = 10;
301 amrex::ParticleReal Erel_cumm = 0.0_prt;
302 amrex::ParticleReal fmult_fact = 1.0_prt;
303 amrex::ParticleReal energy_frac_eff = 0.0_prt;
304 T_index i1 = cell_start;
305 bool correction_failed = false;
306 bool deltaE_is_zero = false;
307 while (!deltaE_is_zero && !correction_failed && loop_count <= max_loop_count) {
308
309 T_index i2 = i1 + 1;
310 if (i2 == cell_stop) {
311 // If i1 is the last particle, set the other particle, i2, to be the first particle
312 i2 = cell_start;
313 }
314
315 int const sign = (deltaE < 0.0 ? -1 : 1);
316
317 const amrex::ParticleReal ux1 = uxp[ indices[i1] ];
318 const amrex::ParticleReal uy1 = uyp[ indices[i1] ];
319 const amrex::ParticleReal uz1 = uzp[ indices[i1] ];
320 const amrex::ParticleReal ux2 = uxp[ indices[i2] ];
321 const amrex::ParticleReal uy2 = uyp[ indices[i2] ];
322 const amrex::ParticleReal uz2 = uzp[ indices[i2] ];
323 const amrex::ParticleReal wpmp1 = mass*w[ indices[i1] ];
324 const amrex::ParticleReal wpmp2 = mass*w[ indices[i2] ];
325
326 // For the calculations below, since it involves taking differences
327 // between similar numbers, which can lose precision, to achieve machine
328 // level accuracy on the energy conservation long double precision would
329 // be required. However, long double is implemented inconsistently across
330 // platforms, so standard precision is used here with the accordant loss
331 // of precision. For practical purposes however, the loss of precision is negligible.
332
333 const amrex::ParticleReal ux = ux1 - ux2;
334 const amrex::ParticleReal uy = uy1 - uy2;
335 const amrex::ParticleReal uz = uz1 - uz2;
336 const amrex::ParticleReal dbetasq = (ux*ux + uy*uy + uz*uz)/c2;
337 const amrex::ParticleReal gbsq1 = (ux1*ux1 + uy1*uy1 + uz1*uz1)/c2;
338 const amrex::ParticleReal gbsq2 = (ux2*ux2 + uy2*uy2 + uz2*uz2)/c2;
339 const amrex::ParticleReal gamma1 = std::sqrt(1.0_prt + gbsq1);
340 const amrex::ParticleReal gamma2 = std::sqrt(1.0_prt + gbsq2);
341 const amrex::ParticleReal E1 = wpmp1*gamma1*c2;
342 const amrex::ParticleReal E2 = wpmp2*gamma2*c2;
343 const amrex::ParticleReal Etot = E1 + E2;
344 const amrex::ParticleReal pxtot = wpmp1*ux1 + wpmp2*ux2;
345 const amrex::ParticleReal pytot = wpmp1*uy1 + wpmp2*uy2;
346 const amrex::ParticleReal pztot = wpmp1*uz1 + wpmp2*uz2;
347 const amrex::ParticleReal Ecm = std::sqrt(Etot*Etot - (pxtot*pxtot + pytot*pytot + pztot*pztot)*c2);
348 const amrex::ParticleReal Erel = Ecm - wpmp1*c2 - wpmp2*c2;
349 if (Erel <= 0.0) { continue; }
350
351 // Set the amount of energy change for this pair based on the
352 // relative energy in the center of momentum.
353 // If deltaE > 0, no more that Erel can be removed from this pair.
354 // If deltaE < 0, limit the energy added by Erel at first, but
355 // after the first loop, add whatever is left (using the adjusted fmult_fact).
356 amrex::ParticleReal deltaEpair = static_cast<amrex::ParticleReal>(sign*energy_fraction*fmult_fact)*Erel;
357 if (std::abs(deltaEpair) >= std::abs(deltaE)) {
358 deltaEpair = deltaE;
359 deltaE = 0.0_prt;
360 deltaE_is_zero = true;
361 } else {
362 deltaE -= deltaEpair;
363 }
364
365 const amrex::ParticleReal A = Etot - deltaEpair;
366 const amrex::ParticleReal D = A*A + E2*E2 - E1*E1;
367 const amrex::ParticleReal p2dotu = static_cast<amrex::ParticleReal>(wpmp2*(ux2*ux + uy2*uy + uz2*uz));
368 const amrex::ParticleReal ptdotu = (pxtot*ux + pytot*uy + pztot*uz);
369
370 // Compute coefficients for quadratic equation for alpha.
371 const amrex::ParticleReal a = A*A*dbetasq - ptdotu*ptdotu;
372 const amrex::ParticleReal b = D*ptdotu - 2.0_prt*A*A*p2dotu;
373 const amrex::ParticleReal c = A*A*E2*E2 - D*D/4.0_prt;
374
375 const amrex::ParticleReal root = b*b - 4.0_prt*a*c;
376 if (root < 0.0 || a == 0.0) { continue; }
377 const amrex::ParticleReal alpha = (-b + std::sqrt(root))/(2.0_prt*a);
378
379 // Update particle velocities.
380 const amrex::ParticleReal ratio1 = alpha/(wpmp1*c2);
381 const amrex::ParticleReal ratio2 = alpha/(wpmp2*c2);
382 uxp[ indices[i1] ] += ratio1*ux;
383 uyp[ indices[i1] ] += ratio1*uy;
384 uzp[ indices[i1] ] += ratio1*uz;
385 uxp[ indices[i2] ] -= ratio2*ux;
386 uyp[ indices[i2] ] -= ratio2*uy;
387 uzp[ indices[i2] ] -= ratio2*uz;
388
389 Erel_cumm += Erel - deltaEpair;
390
391 if (i1 < cell_stop - 2) {
392 // If not done with the loop, increment to the next pair of particles
393 i1 = i2 + 1;
394 } else {
395 loop_count++;
396 // On next time through the loop, use a different set of pairs.
397 // What index to start with depends on whether the number of particles
398 // is even or odd.
399 if (i1 == cell_stop - 2) {
400 if ((numCell % 2) == 0) { i1 = cell_start + 1; }
401 if ((numCell % 2) != 0) { i1 = cell_start; }
402 } else {
403 if ((numCell % 2) == 0) { i1 = cell_start; }
404 if ((numCell % 2) != 0) { i1 = cell_start + 1; }
405 }
406 // Compute the energy fraction to correct with one more loop.
407 // This is the remaining energy correction divided by the cummulated Erel,
408 // the amount of "free" energy available.
409 // If deltaE < 0, this check always passes since the amount of energy that
410 // can be added is unlimited.
411 energy_frac_eff = std::abs(deltaE)/Erel_cumm;
412 if (energy_frac_eff > energy_fraction_max || loop_count > max_loop_count) {
413 correction_failed = true;
414 break;
415 } else if (deltaE < 0.) {
416 // If after the first time around, there is energy left to distribute,
417 // increase the multiplier to distribute the rest faster.
418 fmult_fact = std::max(1._prt, energy_frac_eff/energy_fraction);
419 }
420 }
421
422 }
423
424 return correction_failed;
425 }
426}
427
428#endif // WARPX_PARTICLE_UTILS_H_
#define AMREX_RESTRICT
#define AMREX_INLINE
#define AMREX_GPU_HOST_DEVICE
#define AMREX_D_TERM(a, b, c)
@ vz
Definition RigidInjectedParticleContainer.H:27
@ alpha
Definition SpeciesPhysicalProperties.H:18
__host__ __device__ const Real * lo() const &noexcept
__host__ __device__ const Real * hi() const &noexcept
Definition ParticleUtils.cpp:24
AMREX_GPU_HOST_DEVICE AMREX_INLINE void doLorentzTransformWithP(amrex::ParticleReal &px, amrex::ParticleReal &py, amrex::ParticleReal &pz, amrex::ParticleReal mass, amrex::ParticleReal const Ux, amrex::ParticleReal const Uy, amrex::ParticleReal const Uz)
Perform a Lorentz transformation of the given momentum to a frame moving with gamma*velocity (Ux,...
Definition ParticleUtils.H:159
AMREX_GPU_HOST_DEVICE AMREX_INLINE void doLorentzTransformWithU(amrex::ParticleReal &ux, amrex::ParticleReal &uy, amrex::ParticleReal &uz, amrex::ParticleReal const Ux, amrex::ParticleReal const Uy, amrex::ParticleReal const Uz)
Perform a Lorentz transformation of the given velocity to a frame moving with gamma*velocity (Ux,...
Definition ParticleUtils.H:122
AMREX_GPU_HOST_DEVICE AMREX_INLINE void getCollisionEnergy(amrex::ParticleReal const u2, double const m, double const M, double &gamma, double &energy)
Return (relativistic) collision energy assuming the target (with mass M) is stationary and the projec...
Definition ParticleUtils.H:53
AMREX_GPU_HOST_DEVICE AMREX_INLINE void doLorentzTransform(amrex::ParticleReal &ux, amrex::ParticleReal &uy, amrex::ParticleReal &uz, amrex::ParticleReal const Vx, amrex::ParticleReal const Vy, amrex::ParticleReal const Vz)
Perform a Lorentz transformation of the given velocity to a frame moving with velocity (Vx,...
Definition ParticleUtils.H:78
typename WarpXParticleContainer::ParticleTileType ParticleTileType
Definition ParticleUtils.H:22
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:221
amrex::DenseBins< ParticleTileDataType > findParticlesInEachCell(amrex::Geometry const &geom_lev, amrex::MFIter const &mfi, ParticleTileType &ptile)
Find the particles and count the particles that are in each cell. More specifically this function ret...
Definition ParticleUtils.cpp:35
AMREX_GPU_HOST_DEVICE AMREX_INLINE bool ModifyEnergyPairwise(amrex::ParticleReal *const AMREX_RESTRICT uxp, amrex::ParticleReal *const AMREX_RESTRICT uyp, amrex::ParticleReal *const AMREX_RESTRICT uzp, amrex::ParticleReal *const AMREX_RESTRICT w, T_index const *const AMREX_RESTRICT indices, T_index const cell_start, T_index const cell_stop, amrex::ParticleReal const mass, amrex::ParticleReal const energy_fraction, amrex::ParticleReal const energy_fraction_max, amrex::ParticleReal &deltaE)
Definition ParticleUtils.H:272
AMREX_GPU_HOST_DEVICE AMREX_INLINE void getRandomVector(amrex::ParticleReal &x, amrex::ParticleReal &y, amrex::ParticleReal &z, amrex::RandomEngine const &engine)
Generate random unit vector in 3 dimensions https://mathworld.wolfram.com/SpherePointPicking....
Definition ParticleUtils.H:196
AMREX_GPU_HOST_DEVICE AMREX_INLINE bool containsInclusive(amrex::RealBox const &tilebox, amrex::XDim3 const point)
Definition ParticleUtils.H:243
typename ParticleTileType::ParticleTileDataType ParticleTileDataType
Definition ParticleUtils.H:23
static constexpr auto c
vacuum speed of light [m/s]
Definition constant.H:44
static constexpr auto q_e
elementary charge [C]
Definition constant.H:52
static constexpr amrex::Real pi
ratio of a circle's circumference to its diameter
Definition constant.H:23
Real Random()
__host__ __device__ GpuComplex< T > sqrt(const GpuComplex< T > &a_z) noexcept