79 DColl E{
"",
"E", 10, 6, 6};
80 DColl B{
"",
"B", 10, 6, 6};
86 Vector x_min{-1.0,-1.0,-1.0};
88 Vector p_min{-1.0,-1.0,-1.0};
92 int redist_interval = 5;
94 std::string device_config =
"cpu";
124 std::unique_ptr<ParticleSet> charged_particles;
127 mutable Vector pxB_, pm_, pp_;
137 void FindParticles();
141 void EvaluateFieldsAtParticles();
155 void Redistribute(
int redist_mesh,
Array<int> &removed_idxs);
158 ParticleSet& GetParticles() {
return *charged_particles; }
170 int pad_digits_cycle,
int pad_digits_rank,
int cycle,
171 std::unique_ptr<VisItDataCollection> &dc,
180int main(
int argc,
char *argv[])
189 bool visualization =
true;
190 int vis_tail_size = 5;
191 int vis_interval = 4;
194 args.
AddOption(&
ctx.E.coll_name,
"-er",
"--e-root-file",
195 "Set the VisIt data collection E field root file prefix.");
196 args.
AddOption(&
ctx.E.field_name,
"-ef",
"--e-field-name",
197 "Set the VisIt data collection E field name");
199 "Set the E field cycle index to read.");
200 args.
AddOption(&
ctx.E.pad_digits_cycle,
"-epdc",
"--e-pad-digits-cycle",
201 "Number of digits in E field cycle.");
202 args.
AddOption(&
ctx.E.pad_digits_rank,
"-epdr",
"--e-pad-digits-rank",
203 "Number of digits in E field MPI rank.");
204 args.
AddOption(&
ctx.B.coll_name,
"-br",
"--b-root-file",
205 "Set the VisIt data collection B field root file prefix.");
206 args.
AddOption(&
ctx.B.field_name,
"-bf",
"--b-field-name",
207 "Set the VisIt data collection B field name");
209 "Set the B field cycle index to read.");
210 args.
AddOption(&
ctx.B.pad_digits_cycle,
"-bpdc",
"--b-pad-digits-cycle",
211 "Number of digits in B field cycle.");
212 args.
AddOption(&
ctx.B.pad_digits_rank,
"-bpdr",
"--b-pad-digits-rank",
213 "Number of digits in B field MPI rank.");
214 args.
AddOption(&
ctx.redist_interval,
"-rdf",
"--redist-interval",
215 "Redistribution after this many timesteps. 0 means "
216 "no redistribution.");
217 args.
AddOption(&
ctx.redist_mesh,
"-rdm",
"--redistribution-mesh",
218 "Particle domain mesh for redistribution. 0 for E field mesh."
219 " 1 for B field mesh.");
221 "Ordering of particle data. 0 = byNODES, 1 = byVDIM.");
223 "Total number of particles.");
224 args.
AddOption(&
ctx.m,
"-m",
"--mass",
"Particles' mass.");
225 args.
AddOption(&
ctx.q,
"-q",
"--charge",
"Particles' charge.");
227 "Minimum initial particle location.");
229 "Maximum initial particle location.");
231 "Minimum initial particle momentum.");
233 "Maximum initial particle momentum.");
234 args.
AddOption(&
ctx.dt,
"-dt",
"--time-step",
"Time Step.");
235 args.
AddOption(&
ctx.nt,
"-nt",
"--num-timesteps",
"Number of timesteps.");
236 args.
AddOption(&visualization,
"-vis",
"--visualization",
"-no-vis",
237 "--no-visualization",
238 "Enable or disable GLVis visualization.");
239 args.
AddOption(&vis_tail_size,
"-vt",
"--vis-tail-size",
240 "GLVis visualization trajectory truncation tail size.");
241 args.
AddOption(&vis_interval,
"-vf",
"--vis-interval",
242 "GLVis visualization update after this many timesteps. "
243 "0 means no visualization.");
245 "Device configuration definition string.");
266 std::unique_ptr<VisItDataCollection> E_dc, B_dc;
271 if (
ctx.E.coll_name !=
"")
274 ctx.E.pad_digits_cycle,
ctx.E.pad_digits_rank,
275 ctx.E.cycle, E_dc, E_gf))
277 mfem::err <<
"Error loading E field" << endl;
285 if (
ctx.B.coll_name !=
"")
288 ctx.B.pad_digits_cycle,
ctx.B.pad_digits_rank,
289 ctx.B.cycle, B_dc, B_gf))
291 mfem::err <<
"Error loading B field" << endl;
294 Vector bb_xmint, bb_xmaxt;
295 B_gf->ParFESpace()->GetParMesh()->GetBoundingBox(bb_xmint, bb_xmaxt, 2);
297 if (
ctx.E.coll_name !=
"")
300 for (
int d = 0; d < bb_xmin.
Size(); d++)
302 bb_xmin[d] = std::max(bb_xmin[d], bb_xmint[d]);
303 bb_xmax[d] = std::min(bb_xmax[d], bb_xmaxt[d]);
317 int num_particles =
ctx.npt/num_ranks +
318 (rank < (
ctx.npt % num_ranks) ? 1 : 0);
319 Boris boris(MPI_COMM_WORLD, E_gf, B_gf, num_particles, ordering_type,
325 boris.FindParticles();
326 boris.Redistribute(
ctx.redist_mesh, removed_idxs_dummy);
327 boris.FindParticles();
328 boris.EvaluateFieldsAtParticles();
336 std::unique_ptr<ParticleTrajectories> traj_vis;
337 bool do_vis = visualization && (vis_interval > 0);
340 const char *keys =
"baaa";
341 traj_vis = std::make_unique<ParticleTrajectories>(boris.GetParticles(),
345 0, 0, 600, 600, keys);
346 traj_vis->SetVisualizationBoundingBox(bb_xmin, bb_xmax);
349 for (
int step = 1; step <=
ctx.nt; step++)
354 boris.StepDevice(t, dt);
362 mfem::out <<
"Step: " << step <<
" | Time: " << t << endl;
366 if (do_vis && step % vis_interval == 0)
368 traj_vis->Visualize();
372 Array<int> removed_idxs = boris.RemoveLostParticles();
374 bool particles_removed = removed_idxs.
Size() > 0;
375 MPI_Allreduce(MPI_IN_PLACE, &particles_removed, 1, MFEM_MPI_CXX_BOOL,
376 MPI_LOR, boris.GetParticles().GetComm());
379 bool redistributed =
false;
380 if (
ctx.redist_interval > 0 && step %
ctx.redist_interval == 0 &&
381 boris.GetParticles().GetGlobalNParticles() > 0)
385 boris.Redistribute(
ctx.redist_mesh, removed_idxs);
386 redistributed =
true;
391 if (particles_removed || redistributed)
393 boris.FindParticles();
408 add(
p, 0.5 * dt * q, e, pm_);
414 const real_t a1 = 4.0 * dt * q * m;
419 const real_t a2 = 4.0 * m * m -
420 dt * dt * q * q * B2;
424 const real_t a3 = 2.0 * dt * dt * q * q * (
b * pm_);
428 const real_t a4 = 4.0 * m * m +
429 dt * dt * q * q * B2;
433 add(pp_, 0.5 * dt * q, e,
p);
446 MFEM_VERIFY(E_gf || B_gf,
"Must pass an E field or B field to Boris.");
448 Mesh *E_mesh = E_gf ? E_gf->FESpace()->GetMesh() :
nullptr;
449 Mesh *B_mesh = B_gf ? B_gf->FESpace()->GetMesh() :
nullptr;
450 if (E_mesh && B_mesh)
454 MFEM_VERIFY(E_dim == B_dim,
455 "E mesh and B mesh must have the same spatial dimension.");
460 E_finder.Setup(*E_mesh);
465 B_finder.Setup(*B_mesh);
469 MFEM_VERIFY(
dim == 3,
"Only 3D meshes are currently supported.");
471 pxB_.SetSize(
dim); pm_.SetSize(
dim); pp_.SetSize(
dim);
478 charged_particles = std::make_unique<ParticleSet>
479 (comm, nparticles,
dim, field_vdims, 0, pdata_ordering,
483void Boris::FindParticles()
498void Boris::EvaluateFieldsAtParticles()
525 EvaluateFieldsAtParticles();
528 if (charged_particles->IsParticleRefValid())
530 for (
int i = 0; i < charged_particles->GetNParticles(); i++)
532 Particle p = charged_particles->GetParticleRef(i);
538 for (
int i = 0; i < charged_particles->GetNParticles(); i++)
540 Particle p = charged_particles->GetParticle(i);
542 charged_particles->SetParticle(i,
p);
556 EvaluateFieldsAtParticles();
557 const int N = charged_particles->GetNParticles();
558 auto &X = charged_particles->Coords();
559 auto &M = charged_particles->Field(MASS);
560 auto &Q = charged_particles->Field(CHARGE);
561 auto &P = charged_particles->Field(MOM);
562 auto &E = charged_particles->Field(EFIELD);
563 auto &B = charged_particles->Field(BFIELD);
576 auto d_p = P.ReadWrite();
587 for (
int d = 0; d <
dim; d++)
589 x[d] = d_x[byVDIM_X ? i *
dim + d : i + d * N];
590 p[d] = d_p[byVDIM_P ? i *
dim + d : i + d * N];
591 e[d] = d_e[byVDIM_E ? i *
dim + d : i + d * N];
592 b[d] = d_b[byVDIM_B ? i *
dim + d : i + d * N];
596 real_t pm[3], pxB[3], pp[3];
600 for (
int d = 0; d <
dim; d++)
602 pm[d] =
p[d] + (0.5 * dt * q) * e[d];
607 for (
int d = 0; d <
dim; d++) { B2 +=
b[d] *
b[d]; }
611 pxB[0] = pm[1] *
b[2] - pm[2] *
b[1];
612 pxB[1] = pm[2] *
b[0] - pm[0] *
b[2];
613 pxB[2] = pm[0] *
b[1] - pm[1] *
b[0];
616 const real_t a1 = 4.0 * dt * q * m;
617 for (
int d = 0; d <
dim; d++) { pp[d] = a1 * pxB[d]; }
621 const real_t a2 = 4.0 * m * m - dt * dt * q * q * B2;
622 for (
int d = 0; d <
dim; d++) { pp[d] += a2 * pm[d]; }
626 for (
int d = 0; d <
dim; d++) { b_dot_pm +=
b[d] * pm[d]; }
627 const real_t a3 = 2.0 * dt * dt * q * q * b_dot_pm;
629 for (
int d = 0; d <
dim; d++) { pp[d] += a3 *
b[d]; }
632 const real_t a4 = 4.0 * m * m + dt * dt * q * q * B2;
633 for (
int d = 0; d <
dim; d++) { pp[d] /= a4; }
637 for (
int d = 0; d <
dim; d++)
639 p[d] = pp[d] + (0.5 * dt * q) * e[d];
645 for (
int d = 0; d <
dim; d++)
647 d_p[byVDIM_P ? i *
dim + d : i + d * N] =
p[d];
648 d_x[byVDIM_X ? i *
dim + d : i + d * N] = x[d] + (dt / m) *
p[d];
665 for (
const int &elem : E_lost)
667 lost_idxs.
Union(elem);
670 for (
const int &elem : B_lost)
672 lost_idxs.
Union(elem);
675 charged_particles->RemoveParticles(lost_idxs);
679void Boris::Redistribute(
int redist_mesh,
Array<int> &removed_idxs)
681 if (redist_mesh == 0 && E_gf)
685 charged_particles->Redistribute(proc_list);
691 charged_particles->Redistribute(proc_list);
699 <<
" | | ___________ ____ _____/ |_________"
701 <<
" | | / _ \\_ __ \\_/ __ \\ / \\ __\\___ /"
703 <<
" | |__( <_> ) | \\/\\ ___/| | \\ | / / "
705 <<
" |_______ \\____/|__| \\___ >___| /__| /_____ \\"
707 <<
" \\/ \\/ \\/ \\/"
712 int pad_digits_cycle,
int pad_digits_rank,
int cycle,
715 dc = std::make_unique<VisItDataCollection>(MPI_COMM_WORLD, coll_name);
716 dc->SetPadDigitsCycle(pad_digits_cycle);
717 dc->SetPadDigitsRank(pad_digits_rank);
722 mfem::err <<
"Error loading VisIt data collection: "
723 << coll_name << endl;
727 if (dc->HasField(field_name))
729 gf = dc->GetParField(field_name);
741 MPI_Comm_rank(charged_particles.
GetComm(), &rank);
742 std::mt19937 gen(rank);
745 std::uniform_real_distribution<real_t> real_dist_x(0_r,1_r);
750 add(0.5, p_min, p_max, p_center);
751 Vector dp = p_max; dp -= p_min; dp *= 1_r/6_r;
752 std::vector<std::normal_distribution<real_t>> norm_dist_p;
753 for (
int d = 0; d <
dim; d++)
755 norm_dist_p.emplace_back(p_center[d], dp[d] > 0_r ? dp[d] : 1_r);
770 for (
int d = 0; d <
dim; d++)
772 if (x_min[d] >= x_max[d]) { X(i,d) = x_min[d]; }
775 X(i,d) = x_min[d] + real_dist_x(gen)*(x_max[d] - x_min[d]);
779 if (p_min[d] >= p_max[d]) { P(i,d) = p_min[d]; }
782 real_t p_val = norm_dist_p[d](gen);
783 while (p_val < p_min[d] || p_val > p_max[d])
785 p_val = norm_dist_p[d](gen);
int Union(const T &el)
Append element when it is not yet in the array, return index.
int Size() const
Return the logical size of the array.
void DeleteAt(const Array< int > &indices)
Delete entries at indices, and resize.
The MFEM Device class abstracts hardware devices such as GPUs, as well as programming models such as ...
void Print(std::ostream &os=mfem::out)
Print the configuration of the MFEM virtual device object.
static bool IsEnabled()
Return true if any backend other than Backend::CPU is enabled.
FindPointsGSLIB can robustly evaluate a GridFunction on an arbitrary collection of points....
void FindPoints(const Vector &point_pos, int point_pos_ordering=Ordering::byNODES)
Searches positions given in physical space by point_pos.
virtual void Interpolate(const GridFunction &field_in, Vector &field_out)
Interpolation of field values at prescribed reference space positions.
Array< unsigned int > GetPointsNotFoundIndices() const
Get array of indices of not-found points.
virtual const Array< unsigned int > & GetProc() const
Return MPI rank on which each point was found by FindPoints.
Class for grid function - Vector with associated FE space.
static void Init()
Initialize hypre by calling HYPRE_Init() and set default options. After calling Hypre::Init(),...
void EnsureNodes()
Make sure that the mesh has valid nodes, i.e. its geometry is described by a vector finite element gr...
int SpaceDimension() const
Dimension of the physical space containing the mesh.
static bool Root()
Return true if the rank in MPI_COMM_WORLD is zero.
static int WorldRank()
Return the MPI rank in MPI_COMM_WORLD.
static int WorldSize()
Return the size of MPI_COMM_WORLD.
static void Init(int &argc, char **&argv, int required=default_thread_required, int *provided=nullptr)
Singleton creation with Mpi::Init(argc, argv).
void Parse()
Parse the command-line options. Note that this function expects all the options provided through the ...
void PrintUsage(std::ostream &out) const
Print the usage message.
void PrintOptions(std::ostream &out) const
Print the options.
void AddOption(bool *var, const char *enable_short_name, const char *enable_long_name, const char *disable_short_name, const char *disable_long_name, const char *description, bool required=false)
Add a boolean option and set 'var' to receive the value. Enable/disable tags are used to set the bool...
bool Good() const
Return true if the command line options were parsed successfully.
ParMesh * GetParMesh() const
Class for parallel grid function.
ParFiniteElementSpace * ParFESpace() const
void GetBoundingBox(Vector &p_min, Vector &p_max, int ref=2)
ParticleSet initializes and manages data associated with particles.
MPI_Comm GetComm() const
Get the MPI communicator for this ParticleSet.
ParticleVector & Coords()
Get a reference to the coordinates ParticleVector.
ParticleVector & Field(int f)
Get a reference to field f 's ParticleVector.
int GetNParticles() const
Get the number of active particles currently held by this ParticleSet.
ParticleVector carries vector data (of a given vector dimension) for an arbitrary number of particles...
int GetVDim() const
Get the Vector dimension of the ParticleVector.
Ordering::Type GetOrdering() const
Get the ordering of data in the ParticleVector.
Container for data associated with a single particle.
Vector & Field(int f)
Get reference to field f Vector.
real_t & FieldValue(int f, int c=0)
Get reference to field f , component c value.
Vector & Coords()
Get reference to particle coordinates Vector.
virtual const real_t * Read(bool on_dev=true) const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), on_dev).
virtual real_t * ReadWrite(bool on_dev=true)
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), on_dev).
Vector & Set(const real_t a, const Vector &x)
(*this) = a * x
int Size() const
Returns the size of the vector.
virtual void UseDevice(bool use_dev) const
Enable execution of Vector operations using the mfem::Device.
virtual real_t * HostWrite()
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), false).
Vector & Add(const real_t a, const Vector &Va)
(*this) += a * Va
void cross3D(const Vector &vin, Vector &vout) const
int ReadGridFunction(std::string coll_name, std::string field_name, int pad_digits_cycle, int pad_digits_rank, int cycle, std::unique_ptr< VisItDataCollection > &dc, ParGridFunction *&gf)
struct LorentzContext ctx
void InitializeChargedParticles(ParticleSet &particles, const Vector &pos_min, const Vector &pos_max, const Vector &x_init, const Vector &p_init, real_t m, real_t q)
void display_banner(ostream &os)
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
void add(const Vector &v1, const Vector &v2, Vector &v)
OutStream err(std::cerr)
Global stream used by the library for standard error output. Initially it uses the same std::streambu...
void forall(int N, lambda &&body)
real_t p(const Vector &x, real_t t)