Skip to content

Commit df9f2bf

Browse files
authored
Merge pull request #389 from JamesMcClung/pr/randomize-prts
Support randomized particle locations at t=0
2 parents deea22c + 3b245dd commit df9f2bf

4 files changed

Lines changed: 138 additions & 80 deletions

File tree

src/include/boundary_injector.hxx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ public:
2323

2424
// FIXME would be nice to just pass 1 thing for kind-related info
2525
ParticleGeneratorMaxwellian(int kind_idx, Grid_t::Kind kind, Real3 mean_u,
26-
Real3 temperature)
27-
: kind_idx{kind_idx}
26+
Real3 temperature, bool correct_gamma = false)
27+
: kind_idx{kind_idx}, correct_gamma{correct_gamma}
2828
{
2929
for (int d = 0; d < 3; d++) {
3030
Real stdev_u = sqrt(temperature[d] / kind.m);
@@ -40,6 +40,9 @@ public:
4040
}
4141

4242
Real3 u{vdfs[0].get(), vdfs[1].get(), vdfs[2].get()};
43+
if (correct_gamma) {
44+
u = vel_to_4vel(u);
45+
}
4346
Real w = 1.0;
4447
psc::particle::Tag tag = 0;
4548

@@ -50,6 +53,7 @@ private:
5053
using VelocityDistributionFunction = rng::Normal<Real>;
5154
Vec3<VelocityDistributionFunction> vdfs;
5255
int kind_idx;
56+
bool correct_gamma;
5357
rng::Uniform<Real> uniform_dist{0.0, 1.0};
5458
};
5559

src/include/psc/moment.hxx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include "psc_bits.h"
77
#include <psc/deposit.hxx>
8+
#include "fields_item.hxx"
89

910
namespace psc
1011
{

src/include/setup_particles.hxx

Lines changed: 85 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
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

3645
struct 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

src/libpsc/tests/test_mparticles.cxx

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11

22
#include <gtest/gtest.h>
3+
#include <gtensor/reductions.h>
34

45
#include "test_common.hxx"
56

@@ -8,6 +9,7 @@
89
#include "../libpsc/vpic/PscParticlesBase.h"
910
#include "../libpsc/vpic/mparticles_vpic.hxx"
1011
#include "../libpsc/vpic/vpic_config.h"
12+
#include "../libpsc/psc_output_fields/fields_item_moments_1st.hxx"
1113
#include "psc_particles_double.h"
1214
#include "psc_particles_single.h"
1315
#include "particle_with_id.h"
@@ -453,7 +455,7 @@ TEST(TestSetupParticles, Simple)
453455
Mparticles mprts{grid};
454456

455457
SetupParticles<Mparticles> setup_particles(grid);
456-
setup_particles(
458+
setup_particles.setupParticles(
457459
mprts, [&](int kind, Double3 crd, psc_particle_npt& npt) { npt.n = 1; });
458460

459461
auto n_cells =
@@ -474,11 +476,12 @@ TEST(TestSetupParticles, NPopulations)
474476

475477
int n_populations = 2;
476478
SetupParticles<Mparticles> setup_particles(grid, n_populations);
477-
setup_particles(mprts, [&](int pop, Double3 crd, psc_particle_npt& npt) {
478-
npt.n = 1;
479-
npt.kind = 0;
480-
npt.p[0] = double(pop); // save pop in u[0] for testing
481-
});
479+
setup_particles.setupParticles(
480+
mprts, [&](int pop, Double3 crd, psc_particle_npt& npt) {
481+
npt.n = 1;
482+
npt.kind = 0;
483+
npt.p[0] = double(pop); // save pop in u[0] for testing
484+
});
482485

483486
auto n_cells =
484487
grid.domain.gdims[0] * grid.domain.gdims[1] * grid.domain.gdims[2];
@@ -497,7 +500,7 @@ TEST(TestSetupParticles, Id)
497500
Mparticles mprts{grid};
498501

499502
SetupParticles<Mparticles> setup_particles(grid);
500-
setup_particles(
503+
setup_particles.setupParticles(
501504
mprts, [&](int kind, Double3 crd, psc_particle_npt& npt) { npt.n = 1; });
502505

503506
auto n_cells =
@@ -525,10 +528,11 @@ TEST(TestSetupParticles, Tag)
525528
Mparticles mprts{grid};
526529

527530
SetupParticles<Mparticles> setup_particles(grid);
528-
setup_particles(mprts, [&](int kind, Double3 crd, psc_particle_npt& npt) {
529-
npt.n = 1;
530-
npt.tag = psc::particle::Tag{kind * 10};
531-
});
531+
setup_particles.setupParticles(
532+
mprts, [&](int kind, Double3 crd, psc_particle_npt& npt) {
533+
npt.n = 1;
534+
npt.tag = psc::particle::Tag{kind * 10};
535+
});
532536

533537
auto n_cells =
534538
grid.domain.gdims[0] * grid.domain.gdims[1] * grid.domain.gdims[2];
@@ -541,14 +545,43 @@ TEST(TestSetupParticles, Tag)
541545
}
542546
}
543547

548+
TEST(TestSetupParticles, RandomOffsets)
549+
{
550+
using Mparticles = MparticlesDouble;
551+
using a = Mfields<double>;
552+
using MomentRho =
553+
psc::moment::moment_rho<psc::deposit::code::Deposit1stNc, dim_yz>;
554+
555+
auto domain = Grid_t::Domain{{1, 2, 2}, {10., 20., 20.}, {}, {1, 1, 1}};
556+
auto kinds = Grid_t::Kinds{{1., 100., "i"}, {-1., 1., "e"}};
557+
auto prm = Grid_t::NormalizationParams::dimensionless();
558+
prm.nicell = 10;
559+
Grid_t grid{domain, {}, kinds, {prm}, .1};
560+
Mparticles mprts{grid};
561+
562+
SetupParticles<Mparticles> setup_particles(grid);
563+
setup_particles.random_offsets = true;
564+
setup_particles.setupParticles(
565+
mprts, [&](int kind, Double3 crd, psc_particle_npt& npt) { npt.n = 1; });
566+
567+
auto n_cells =
568+
grid.domain.gdims[0] * grid.domain.gdims[1] * grid.domain.gdims[2];
569+
EXPECT_EQ(mprts.size(), n_cells * kinds.size() * prm.nicell);
570+
571+
auto rho =
572+
psc::mflds::zeros<double, gt::space::host>(mprts.grid(), 1, -grid.ibn);
573+
MomentRho{}(rho, -grid.ibn, mprts);
574+
ASSERT_LT(gt::norm_linf(rho), 1e-16);
575+
}
576+
544577
int main(int argc, char** argv)
545578
{
546579
MPI_Init(&argc, &argv);
547-
//#ifdef USE_VPIC FIXME
580+
// #ifdef USE_VPIC FIXME
548581
MPI_Comm_dup(MPI_COMM_WORLD, &psc_comm_world);
549582
MPI_Comm_rank(psc_comm_world, &psc_world_rank);
550583
MPI_Comm_size(psc_comm_world, &psc_world_size);
551-
//#endif
584+
// #endif
552585

553586
::testing::InitGoogleTest(&argc, argv);
554587
int rc = RUN_ALL_TESTS();

0 commit comments

Comments
 (0)