WarpX
Loading...
Searching...
No Matches
VirtualPhotonCreation.H
Go to the documentation of this file.
1/* Copyright 2025 Arianna Formenti
2 *
3 * This file is part of WarpX.
4 *
5 * License: BSD-3-Clause-LBNL
6 */
7
8#ifndef WARPX_VIRTUAL_PHOTONS_H_
9#define WARPX_VIRTUAL_PHOTONS_H_
10
15
16#include "WarpX.H"
17#include "Utils/TextMsg.H"
19
20#include <AMReX_INT.H>
21#include <AMReX_REAL.H>
22#include <AMReX_Particle.H>
23
24#include <cmath>
25
26
28
29using namespace amrex::literals;
30using SoaData_type = typename WarpXParticleContainer::ParticleTileType::ParticleTileDataType;
31
32
55
56 WARPX_PROFILE("collision::binarycollision::virtualphotons::GenerateVirtualPhotons()");
57
58 // Loop through the species
59 for (int i_s = 0; i_s < mypc->nSpecies(); ++i_s) {
60
61 auto& primary = mypc->GetParticleContainer(i_s);
62
63 if(!primary.has_virtual_photons()){
64 continue;
65 }
66
67 // Get the virtual photon species corresponding to this primary species
68 int vphotons_index = primary.getVirtualPhotonSpeciesIndex();
69 auto& vphotons = mypc->GetParticleContainer(vphotons_index);
70 const amrex::ParmParse pp_species_name(mypc->GetSpeciesNames()[vphotons_index]);
71
72 // Minimum allowed energy of the virtual photons
73 amrex::Real vphoton_min_energy;
74 utils::parser::getWithParser(pp_species_name, "qed_virtual_photons_min_energy", vphoton_min_energy);
75
76 // Sampling factor (a.k.a. multiplier):
77 // the number of virtual photons generated is multiplied by this factor,
78 // the weight of each virtual photon is divided by this factor
79 amrex::Real sampling_factor;
80 utils::parser::getWithParser(pp_species_name, "qed_virtual_photons_multiplier", sampling_factor);
81
82 amrex::Real const alpha_over_pi = PhysConst::alpha / MathConst::pi;
83 amrex::Real const inv_c2 = 1._rt / (PhysConst::c * PhysConst::c);
84 amrex::Real const mass = primary.getMass();
85
86 int const nlevs = std::max(0, primary.finestLevel()+1);
87 for (int lev = 0; lev < nlevs; ++lev) {
88#ifdef AMREX_USE_OMP
89#pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
90#endif
91 for (amrex::MFIter mfi = primary.MakeMFIter(lev); mfi.isValid(); ++mfi)
92 {
93 // Notation: _vp means virtual photon
94 // Primary particles (leptons) in the current tile
95 ParticleTileType& ptile = primary.ParticlesAt(lev, mfi);
96 const auto soa = ptile.getParticleTileData();
97
98 // Number of primary particles in the current tile
99 amrex::Long const num = ptile.numParticles();
100
101 // Vector that will contain the number of virtual photons for each primary particle
103 auto* num_vp_data = num_vp.dataPtr();
104
105 // First pass: compute the number of virtual photons for each primary particle
106 // and fill the corresponding vector
108 [=] AMREX_GPU_DEVICE (amrex::Long i, amrex::RandomEngine const& engine) noexcept
109 {
110 amrex::ParticleReal ux = soa.m_rdata[PIdx::ux][i]; // u=v*gamma=p/m_e
111 amrex::ParticleReal uy = soa.m_rdata[PIdx::uy][i];
112 amrex::ParticleReal uz = soa.m_rdata[PIdx::uz][i];
113
114 // Formula 99.16 in Berestetskii et al., Quantum Electrodynamics
115 // integrated over the photon energies from vphoton_min_energy to the energy of the primary particle
116 // A similar formula is 15.58 in Jackson's, Classical Electrodynamics
117 // but neglect longitudinal field, assume relativistic velocities, and integrate in energy
118 amrex::ParticleReal gamma = std::sqrt( 1.0_rt + (ux*ux + uy*uy + uz*uz) * inv_c2 );
119 // Minimum fractional (w.r.t. the primary) photon energy
120 amrex::ParticleReal y_min = vphoton_min_energy * inv_c2 / (gamma * mass);
121 amrex::ParticleReal lny = std::log( y_min );
122 // Number of virtual photons per primary particle
123 amrex::Real r_photons = alpha_over_pi * lny * lny * sampling_factor;
124
125 // `n_photons` must be an integer, but must average to `r_photons` over many realizations
126 // This is achieved by adding a random number between 0 and 1, and taking the integer part.
127 amrex::Long n_photons = (amrex::Long)( r_photons + amrex::Random(engine) );
128
129 num_vp_data[i] = n_photons;
130 });
131
132 // Compute the offsets vector as the cumulative sum of the elements of num_vp excluding the current element,
133 // i.e., offsets[i] = sum_{j=0}^{i-1} num_vp[j],
134 // and return the total number of virtual photons to be generated in the current tile
135 // (which is the last element of the offsets vector)
137 const amrex::Long total_num_vp = amrex::Scan::ExclusiveSum(num_vp.size(), num_vp.data(), offsets_vp.data());
138 auto *const offset_vp_data = offsets_vp.dataPtr();
139
140 // Now we can allocate and build the virtual photon species in the current tile
141 // Note that this operation will overwrite any virtual photons that were previously generated by mypc
142 // namely the ones that were created in the previous time step.
143 ParticleTileType& ptile_vp = vphotons.ParticlesAt(lev, mfi);
144 ptile_vp.resize(total_num_vp);
145
146 // Get the starting particle ID on CPU and reserve IDs for all virtual photons
147 // This must be done on CPU because NextID() is not thread-safe and cannot be called from GPU
148 amrex::Long pid;
149#ifdef AMREX_USE_OMP
150#pragma omp critical (virtual_photon_nextid)
151#endif
152 {
153 pid = ParticleTileType::ParticleType::NextID();
154 ParticleTileType::ParticleType::NextID(pid + total_num_vp);
155 }
156
157 const int cpuid = amrex::ParallelDescriptor::MyProc();
158
159 // SoA that will contain the virtual photons data
160 auto &soa_vp = ptile_vp.GetStructOfArrays();
161
162 // Array with the PIDs of the virtual photons
163 uint64_t * AMREX_RESTRICT pid_vp = soa_vp.GetIdCPUData().data();
164
165 // Pointers to the arrays that will contain the particle attributes of the virtual photons
167 for (int ia = 0; ia < PIdx::nattribs; ++ia) {
168 pa_vp[ia] = soa_vp.GetRealData(ia).data();
169 }
170
171 // Capture the starting PID for use in the GPU kernel
172 const amrex::Long pid_start = pid;
173
174 // Second pass: populate the virtual photon species
176 [=] AMREX_GPU_DEVICE (amrex::Long i, amrex::RandomEngine const& engine) noexcept
177 {
178 // Primary particle
179 amrex::ParticleReal ux_primary = soa.m_rdata[PIdx::ux][i];
180 amrex::ParticleReal uy_primary = soa.m_rdata[PIdx::uy][i];
181 amrex::ParticleReal uz_primary = soa.m_rdata[PIdx::uz][i];
182 amrex::ParticleReal u_primary = std::sqrt(ux_primary*ux_primary + uy_primary*uy_primary + uz_primary*uz_primary);
183 amrex::ParticleReal nx = ux_primary / u_primary; // normalized ux
184 amrex::ParticleReal ny = uy_primary / u_primary; // normalized uy
185 amrex::ParticleReal nz = uz_primary / u_primary; // normalized uz
186 amrex::ParticleReal gamma_primary = std::sqrt( 1.0_rt + (ux_primary*ux_primary + uy_primary*uy_primary + uz_primary*uz_primary)*inv_c2 );
187
188#if defined (WARPX_DIM_3D)
189 amrex::ParticleReal x = soa.m_rdata[PIdx::x][i];
190 amrex::ParticleReal y = soa.m_rdata[PIdx::y][i];
191 amrex::ParticleReal z = soa.m_rdata[PIdx::z][i];
192#elif defined (WARPX_DIM_XZ)
193 amrex::ParticleReal x = soa.m_rdata[PIdx::x][i];
194 amrex::ParticleReal z = soa.m_rdata[PIdx::z][i];
195#elif defined (WARPX_DIM_RZ)
196 amrex::ParticleReal x = soa.m_rdata[PIdx::x][i];
197 amrex::ParticleReal z = soa.m_rdata[PIdx::z][i];
198 amrex::ParticleReal theta = soa.m_rdata[PIdx::theta][i];
199#elif defined (WARPX_DIM_1D_Z)
200 amrex::ParticleReal z = soa.m_rdata[PIdx::z][i];
201#elif defined (WARPX_DIM_RCYLINDER)
202 amrex::ParticleReal x = soa.m_rdata[PIdx::x][i];
203 amrex::ParticleReal theta = soa.m_rdata[PIdx::theta][i];
204#elif defined(WARPX_DIM_RSPHERE)
205 amrex::ParticleReal x = soa.m_rdata[PIdx::x][i];
206 amrex::ParticleReal theta = soa.m_rdata[PIdx::theta][i];
207 amrex::ParticleReal phi = soa.m_rdata[PIdx::phi][i];
208#endif
209 amrex::ParticleReal w = soa.m_rdata[PIdx::w][i];
210
211 // TODO: add a runtime attribute to the virtual photon species
212 // that containes the pid of the parent particle = soa.m_idcpu[i]
213 // This will allow to update the parent lepton if needed
214
215 // Minimum fractional (wrt primary particle) photon energy
216 amrex::Real y_min = vphoton_min_energy / (mass * gamma_primary * PhysConst::c * PhysConst::c);
217 amrex::Real umin = 0._rt;
218 amrex::Real umax = std::log(y_min) * std::log(y_min);
219
220 for (int j = 0; j < num_vp_data[i]; j++)
221 {
222 // Sample frac_energy from a probability distribution function
223 // that is proportional to log(frac_energy)/frac_energy
224 // (formula 99.16 in Berestetskii et al.)
225 // using the method of the inverse cumulative distributionfunction
226
227 // Draw a random number between umin and umax
228 amrex::ParticleReal rnd = (umax - umin) * amrex::Random(engine) + umin ;
229 // Fractional energy of the photon, often denoted as y (or x)
230 amrex::ParticleReal frac_energy = std::exp( - std::sqrt(rnd) );
231 // Energy of the virtual photon
232 amrex::ParticleReal vphoton_energy = frac_energy * gamma_primary * PhysConst::c;
233
234 // Photon index for the current primary
235 amrex::Long ip = offset_vp_data[i] + j;
236 pa_vp[PIdx::ux][ip] = vphoton_energy * nx; // will be multiplied by m_e before dumping the outputs
237 pa_vp[PIdx::uy][ip] = vphoton_energy * ny; // will be multiplied by m_e before dumping the outputs
238 pa_vp[PIdx::uz][ip] = vphoton_energy * nz; // will be multiplied by m_e before dumping the outputs
239
240 // TODO: add beam size effect - displace the photon position
241#if defined (WARPX_DIM_3D)
242 pa_vp[PIdx::x][ip] = x;
243 pa_vp[PIdx::y][ip] = y;
244 pa_vp[PIdx::z][ip] = z;
245#elif defined (WARPX_DIM_XZ)
246 pa_vp[PIdx::x][ip] = x;
247 pa_vp[PIdx::z][ip] = z;
248#elif defined (WARPX_DIM_RZ)
249 pa_vp[PIdx::x][ip] = x;
250 pa_vp[PIdx::z][ip] = z;
251 pa_vp[PIdx::theta][ip] = theta;
252#elif defined (WARPX_DIM_1D_Z)
253 pa_vp[PIdx::z][ip] = z;
254#elif defined (WARPX_DIM_RCYLINDER)
255 pa_vp[PIdx::x][ip] = x;
256 pa_vp[PIdx::theta][ip] = theta;
257#elif defined(WARPX_DIM_RSPHERE)
258 pa_vp[PIdx::x][ip] = x;
259 pa_vp[PIdx::theta][ip] = theta;
260 pa_vp[PIdx::phi][ip] = phi;
261#endif
262 pa_vp[PIdx::w][ip] = w / sampling_factor;
263 pid_vp[ip] = amrex::SetParticleIDandCPU(pid_start + ip, cpuid);
264 }
265 });
266 } // mfi
267 } // lev
268 } // species
269} // function
270} // close namespace
271#endif // WARPX_VIRTUAL_PHOTONS_H_
#define AMREX_RESTRICT
#define AMREX_GPU_DEVICE
#define WARPX_PROFILE(fname)
Definition WarpXProfilerWrapper.H:13
Definition MultiParticleContainer.H:69
int nSpecies() const
Definition MultiParticleContainer.H:267
WarpXParticleContainer & GetParticleContainer(int index) const
Definition MultiParticleContainer.H:83
std::vector< std::string > GetSpeciesNames() const
Definition MultiParticleContainer.H:320
virtual int getVirtualPhotonSpeciesIndex() const
Definition WarpXParticleContainer.H:537
size_type size() const noexcept
T * dataPtr() noexcept
T * data() noexcept
amrex_real Real
amrex_particle_real ParticleReal
amrex_long Long
T ExclusiveSum(N n, T const *in, T *out, RetSum a_ret_sum=retSum)
PODVector< T, ArenaAllocator< T > > DeviceVector
Real Random()
constexpr auto alpha
fine-structure constant = mu0/(4*pi)*q_e*q_e*c/hbar [dimensionless]
Definition constant.H:177
constexpr auto c
vacuum speed of light [m/s]
Definition constant.H:153
constexpr auto pi
ratio of a circle's circumference to its diameter
Definition constant.H:29
__host__ __device__ std::uint64_t SetParticleIDandCPU(Long id, int cpu) noexcept
AMREX_ATTRIBUTE_FLATTEN_FOR void ParallelForRNG(T n, L const &f) noexcept
Definition VirtualPhotonCreation.H:27
void GenerateVirtualPhotons(MultiParticleContainer *mypc)
Definition VirtualPhotonCreation.H:54
typename WarpXParticleContainer::ParticleTileType::ParticleTileDataType SoaData_type
Definition VirtualPhotonCreation.H:30
void getWithParser(const amrex::ParmParse &a_pp, char const *const str, T &val)
Definition ParserUtils.H:166
@ nattribs
Definition WarpXParticleContainer.H:70
@ 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
__host__ __device__ const T * data() const noexcept
ParticleTileDataType getParticleTileData()
void resize(std::size_t count, GrowthStrategy strategy=GrowthStrategy::Poisson)