77
88#include < mrc_profile.h>
99
10+ #include " kg/VecRange.hxx"
1011#include " centering.hxx"
1112#include " particle.h"
1213#include " rng.hxx"
@@ -28,9 +29,17 @@ struct psc_particle_np
2829 psc::particle::Tag tag;
2930};
3031
31- namespace
32+ /* *
33+ * @brief Calculates gamma * v for the given velocity v.
34+ * @tparam Real real type
35+ * @param v the actual velocity
36+ * @return the spatial components of the corresponding 4-velocity
37+ */
38+ template <typename Real>
39+ Vec3<Real> vel_to_4vel (Vec3<Real> v)
3240{
33- const centering::Centerer defaultCenterer (centering::CC );
41+ Real gamma = 1.0 / sqrt (1.0 - v.mag2 ());
42+ return v * gamma;
3443}
3544
3645struct InitNptFunc
@@ -125,7 +134,7 @@ struct SetupParticles
125134 : kinds_{grid.kinds },
126135 norm_{grid.norm },
127136 n_populations_{n_populations},
128- centerer (defaultCenterer )
137+ centerer (centering:: CC )
129138 {
130139 if (n_populations_ == 0 ) {
131140 n_populations_ = kinds_.size ();
@@ -150,43 +159,35 @@ struct SetupParticles
150159 void op_cellwise (const Grid_t& grid, int patch, InitNpFunc init_np,
151160 OpFunc&& op)
152161 {
153- Int3 ilo = Int3{}, ihi = grid.ldims ;
154-
155- for (int jz = ilo[2 ]; jz < ihi[2 ]; jz++) {
156- for (int jy = ilo[1 ]; jy < ihi[1 ]; jy++) {
157- for (int jx = ilo[0 ]; jx < ihi[0 ]; jx++) {
158- Int3 index{jx, jy, jz};
159-
160- Double3 pos = centerer.get_pos (grid.patches [patch], index);
161- // FIXME, the issue really is that (2nd order) particle pushers
162- // don't handle the invariant dim right
163- for (int d = 0 ; d < 3 ; ++d) {
164- if (grid.isInvar (d)) {
165- pos[d] = grid.patches [patch].get_nc (index[d], d);
166- }
167- }
162+ for (Int3 index : VecRange (Int3{}, grid.ldims )) {
163+ Double3 pos = centerer.get_pos (grid.patches [patch], index);
164+ // FIXME, the issue really is that (2nd order) particle pushers
165+ // don't handle the invariant dim right
166+ for (int d = 0 ; d < 3 ; ++d) {
167+ if (grid.isInvar (d)) {
168+ pos[d] = grid.patches [patch].get_nc (index[d], d);
169+ }
170+ }
168171
169- int n_q_in_cell = 0 ;
170- for (int pop = 0 ; pop < n_populations_; pop++) {
171- psc_particle_np np{};
172- if (pop < kinds_.size ()) {
173- np.kind = pop;
174- }
175- init_np (pop, pos, patch, index, np);
176-
177- int n_in_cell;
178- if (pop != neutralizing_population) {
179- n_in_cell = get_n_in_cell (np.n );
180- n_q_in_cell += kinds_[np.kind ].q * n_in_cell;
181- } else {
182- // FIXME, should handle the case where not the last population
183- // is neutralizing
184- assert (neutralizing_population == n_populations_ - 1 );
185- n_in_cell = -n_q_in_cell / kinds_[np.kind ].q ;
186- }
187- op (n_in_cell, np, pos);
188- }
172+ int n_q_in_cell = 0 ;
173+ for (int pop = 0 ; pop < n_populations_; pop++) {
174+ psc_particle_np np{};
175+ if (pop < kinds_.size ()) {
176+ np.kind = pop;
189177 }
178+ init_np (pop, pos, patch, index, np);
179+
180+ int n_in_cell;
181+ if (pop != neutralizing_population) {
182+ n_in_cell = get_n_in_cell (np.n );
183+ n_q_in_cell += kinds_[np.kind ].q * n_in_cell;
184+ } else {
185+ // FIXME, should handle the case where not the last population
186+ // is neutralizing
187+ assert (neutralizing_population == n_populations_ - 1 );
188+ n_in_cell = -n_q_in_cell / kinds_[np.kind ].q ;
189+ }
190+ op (n_in_cell, np, pos);
190191 }
191192 }
192193 }
@@ -217,29 +218,12 @@ struct SetupParticles
217218 p[i] = dist.get (npt.p [i], beta * std::sqrt (npt.T [i] / m));
218219
219220 if (initial_momentum_gamma_correction) {
220- double p_squared = sqr (p[0 ]) + sqr (p[1 ]) + sqr (p[2 ]);
221- if (p_squared < 1 .) {
222- double gamma = 1 . / sqrt (1 . - p_squared);
223- p *= gamma;
224- }
221+ p = vel_to_4vel (p);
225222 }
226223 return p;
227224 };
228225 }
229226
230- // ----------------------------------------------------------------------
231- // setup_particles
232-
233- void operator ()(Mparticles& mprts, InitNpFunc init_np)
234- {
235- setupParticles (mprts, init_np);
236- }
237-
238- void operator ()(Mparticles& mprts, InitNptFunc init_npt)
239- {
240- setupParticles (mprts, init_npt);
241- }
242-
243227 // ----------------------------------------------------------------------
244228 // initNpt_to_initNp
245229
@@ -284,23 +268,58 @@ struct SetupParticles
284268 }
285269
286270 prof_start (pr);
287- const auto & grid = mprts.grid ();
271+ const Grid_t & grid = mprts.grid ();
288272
289273 // mprts.reserve_all(n_prts_by_patch); FIXME
290274
291275 auto inj = mprts.injector ();
292276
277+ std::vector<rng::Uniform<real_t >> offset_rngs;
278+ if (random_offsets) {
279+ LOG_WARN (
280+ " SetupParticles: enabled random offsets. Initial particle positions "
281+ " will be randomized in each cell, instead of all at cell centers. Each "
282+ " species uses the same rng seed, so if there are the same number of "
283+ " particles of each species in each cell, each species will have the "
284+ " exact same initial position distribution. This results in a charge "
285+ " density of 0 if there are two species with opposite charges, but the "
286+ " resulting charge density is nonzero in general. In the latter, case, "
287+ " take special care to ensure Gauss' law isn't violated." );
288+
289+ int seed = rng::detail::get_process_seed ();
290+ for (int species_count = 0 ; species_count < grid.kinds .size ();
291+ species_count++) {
292+ if (centerer.c == centering::Centering::CC ) {
293+ offset_rngs.push_back ({-0.5 , 0.5 , seed});
294+ } else {
295+ offset_rngs.push_back ({0.0 , 1.0 , seed});
296+ }
297+ }
298+ }
299+
293300 for (int p = 0 ; p < mprts.n_patches (); ++p) {
294301 auto injector = inj[p];
295302
296- op_cellwise (grid, p, init_np,
297- [&](int n_in_cell, psc_particle_np& np, Double3& pos) {
298- for (int cnt = 0 ; cnt < n_in_cell; cnt++) {
299- real_t weight = getWeight (np.n , n_in_cell);
300- auto prt = setupParticle (np, pos, weight);
301- injector (prt);
302- }
303- });
303+ op_cellwise (
304+ grid, p, init_np,
305+ [&](int n_in_cell, psc_particle_np& np, Double3& cell_pos_cc) {
306+ for (int cnt = 0 ; cnt < n_in_cell; cnt++) {
307+ real_t weight = getWeight (np.n , n_in_cell);
308+
309+ Double3 prt_pos = cell_pos_cc;
310+ if (random_offsets) {
311+ auto rng = offset_rngs[np.kind ];
312+ for (int d = 0 ; d < 3 ; d++) {
313+ if (!grid.isInvar (d)) {
314+ prt_pos[d] += rng.get () * grid.domain .dx [d];
315+ }
316+ }
317+ }
318+
319+ auto prt = setupParticle (np, prt_pos, weight);
320+ injector (prt);
321+ }
322+ });
304323 }
305324
306325 prof_stop (pr);
@@ -333,6 +352,7 @@ struct SetupParticles
333352 int neutralizing_population = {-1 };
334353 bool fractional_n_particles_per_cell = {false };
335354 bool initial_momentum_gamma_correction = {false };
355+ bool random_offsets = false ;
336356
337357 centering::Centerer centerer;
338358
0 commit comments