88 int redist_interval = 5;
89 int output_csv_interval = 1000;
91 bool visualization =
true;
93 bool reproduce =
true;
117 std::unique_ptr<ParticleSet> charged_particles;
130 bool reproduce =
false);
133 void FindParticles();
142 ParticleSet& GetParticles() {
return *charged_particles; }
157 real_t neutralizing_const;
159 bool precompute_neutralizing_const =
false;
181 bool precompute_neutralizing_const_ =
false);
201int main(
int argc,
char* argv[])
212 "Spatial dimension (2 or 3)");
214 "Finite element polynomial degree");
216 "Number of elements in the x direction.");
218 "Number of elements in the y direction.");
220 "Number of elements in the z direction.");
221 args.
AddOption(&
ctx.q,
"-q",
"--charge",
"Particle charge.");
222 args.
AddOption(&
ctx.m,
"-m",
"--mass",
"Particle mass.");
223 args.
AddOption(&
ctx.dt,
"-dt",
"--time-step",
"Time Step.");
224 args.
AddOption(&
ctx.nt,
"-nt",
"--num-timesteps",
"Number of timesteps.");
226 "Total number of particles.");
227 args.
AddOption(&
ctx.k,
"-k",
"--k",
"Wave number for initial distribution.");
229 "Perturbation amplitude for initial distribution.");
231 "Ordering of particle data. 0 = byNODES, 1 = byVDIM.");
232 args.
AddOption(&
ctx.redist_interval,
"-rdi",
"--redist-interval",
233 "Redistribution and update E_gf interval. Disabled if <= 0.");
234 args.
AddOption(&
ctx.output_csv_interval,
"-oci",
"--output-csv-interval",
235 "Output CSV interval. Disabled if <= 0.");
236 args.
AddOption(&
ctx.visualization,
"-vis",
"--visualization",
"-no-vis",
237 "--no-visualization",
238 "Enable or disable GLVis visualization.");
239 args.
AddOption(&
ctx.visport,
"-p",
"--send-port",
"Socket for GLVis.");
240 args.
AddOption(&
ctx.reproduce,
"-rep",
"--reproduce",
"-no-rep",
242 "Enable or disable reproducible random seed.");
252 MFEM_VERIFY(
ctx.dim == 2 ||
ctx.dim == 3,
253 "Dimension must be 2 or 3, got " <<
ctx.dim);
254 MFEM_VERIFY(
ctx.alpha >= -1.0 &&
ctx.alpha < 1.0,
255 "Alpha should be in range [-1, 1).");
256 MFEM_VERIFY(
ctx.k > 0.0,
257 "k must be nonzero for displacement initialization.");
259 ctx.L = 2.0 * M_PI /
ctx.k;
263 std::vector<Vector> translations;
283 ParMesh mesh(MPI_COMM_WORLD, periodic_mesh);
285 periodic_mesh.
Clear();
304 FieldSolver field_solver(&phi_fespace, &E_fespace, E_finder,
true);
310 ctx.npt / num_ranks + (rank < (
ctx.npt % num_ranks) ? 1 : 0);
311 ParticleMover particle_mover(MPI_COMM_WORLD, &E_gf, E_finder, num_particles,
313 particle_mover.InitializeChargedParticles(
ctx.k,
ctx.alpha,
ctx.m,
ctx.q,
322 for (
int step = 1; step <=
ctx.nt; step++)
325 if (
ctx.redist_interval > 0 &&
326 (step %
ctx.redist_interval == 0 || step == 1) &&
327 particle_mover.GetParticles().GetGlobalNParticles() > 0)
330 particle_mover.Redistribute();
333 field_solver.UpdatePhiGridFunction(particle_mover.GetParticles(),
336 field_solver.UpdateEGridFunction(phi_gf, E_gf);
339 if (
ctx.visualization)
343 "E_field", 0, 0, 500, 500);
345 "Potential", 500, 0, 500, 500);
350 particle_mover.Step(t, dt,
ctx.L, step == 1);
353 mfem::out <<
"Step: " << step <<
" | Time: " << t;
358 if (
ctx.output_csv_interval > 0 &&
359 (step %
ctx.output_csv_interval == 0 || step == 1))
361 std::string csv_prefix =
"PIC_Part_";
363 std::string file_name =
365 particle_mover.GetParticles().PrintCSV(file_name.c_str(), field_idx,
369 if (
ctx.redist_interval > 0 &&
370 (step %
ctx.redist_interval == 0 || step == 1) &&
371 particle_mover.GetParticles().GetGlobalNParticles() > 0)
377 real_t kinetic_energy = particle_mover.ComputeKineticEnergy(-dt/2.);
378 real_t field_energy = field_solver.ComputeFieldEnergy(E_gf);
383 cout <<
"Kinetic energy: " << kinetic_energy <<
"\t"
384 <<
"Field energy: " << field_energy <<
"\t"
385 <<
"Total energy: " << kinetic_energy + field_energy
391 std::ofstream energy_file(
"energy.csv", std::ios::app);
392 energy_file << setprecision(10) << kinetic_energy <<
","
393 << field_energy <<
"," << kinetic_energy + field_energy
403 : E_gf(E_gf_), E_finder(E_finder_)
405 MFEM_ASSERT(E_gf,
"Must pass an E field to ParticleMover.");
407 int dim = E_gf->ParFESpace()->GetMesh()->SpaceDimension();
415 charged_particles = std::make_unique<ParticleSet>(
416 comm, num_particles,
dim, field_vdims, 1, pdata_ordering);
419void ParticleMover::InitializeChargedParticles(
const real_t& k,
425 MPI_Comm_rank(charged_particles->GetComm(), &rank);
428 reproduce ? rank : (rank + static_cast<unsigned int>(time(nullptr))));
429 std::uniform_real_distribution<> real_dist(0.0, 1.0);
430 std::normal_distribution<> norm_dist(0.0, 1.0);
432 int dim = charged_particles->Coords().GetVDim();
436 ParticleVector& M = charged_particles->Field(ParticleMover::MASS);
437 ParticleVector& Q = charged_particles->Field(ParticleMover::CHARGE);
439 for (
int i = 0; i < charged_particles->GetNParticles(); i++)
442 for (
int d = 0; d <
dim; d++) { P(i, d) = m * norm_dist(gen); }
445 for (
int d = 0; d <
dim; d++) { X(i, d) = real_dist(gen) * L; }
448 for (
int d = 0; d <
dim; d++)
451 x -= (
alpha / k) * std::sin(k * x);
455 if (x < 0) { x += L; }
467void ParticleMover::FindParticles()
469 E_finder.
FindPoints(charged_particles->Coords());
485 const int npt = charged_particles->GetNParticles();
488 for (
int particle = 0; particle < npt; ++particle)
490 for (
int d = 0; d <
dim; ++d)
493 (first_step ? dt / 2.0 : dt) * Q(particle) * E(particle, d);
498 for (
int particle = 0; particle < npt; ++particle)
500 for (
int d = 0; d <
dim; ++d)
502 X(particle, d) += dt / M(particle) * P(particle, d);
503 while (X(particle, d) >= L) { X(particle, d) -= L; }
504 while (X(particle, d) < 0.0) { X(particle, d) += L; }
514void ParticleMover::Redistribute()
516 charged_particles->Redistribute(E_finder.
GetProc());
520real_t ParticleMover::ComputeKineticEnergy(
real_t dt)
const
530 real_t kinetic_energy = 0.0;
531 for (
int p = 0;
p < charged_particles->GetNParticles(); ++
p)
534 for (
int d = 0; d < P.
GetVDim(); ++d)
536 const real_t P_m = P(
p, d) + dt * Q(
p) * E(
p, d);
537 p_square_p += P_m * P_m;
539 kinetic_energy += 0.5 * p_square_p / M(
p);
542 real_t global_kinetic_energy = 0.0;
543 MPI_Allreduce(&kinetic_energy, &global_kinetic_energy, 1,
545 MPI_SUM, charged_particles->GetComm());
546 return global_kinetic_energy;
552 bool precompute_neutralizing_const_)
553 : precompute_neutralizing_const(precompute_neutralizing_const_),
559 real_t local_domain_volume = 0.0;
560 for (
int i = 0; i < pmesh->
GetNE(); i++)
564 MPI_Allreduce(&local_domain_volume, &domain_volume, 1,
572 dm.AddDomainIntegrator(
578 diffusion_matrix = dm.ParallelAssemble();
585 grad_interpolator->Assemble();
589FieldSolver::~FieldSolver()
591 delete diffusion_matrix;
592 delete precomputed_neutralizing_lf;
593 delete grad_interpolator;
603 if (!precompute_neutralizing_const || precomputed_neutralizing_lf ==
nullptr)
607 for (
int p = 0;
p < npt; ++
p)
610 MFEM_ASSERT(code[
p] != 2,
"Particle " <<
p <<
" not found.");
618 neutralizing_const = -global_sum / domain_volume;
621 cout <<
"Total charge: " << global_sum
622 <<
", Domain volume: " << domain_volume
623 <<
", Neutralizing constant: " << neutralizing_const << endl;
624 if (precompute_neutralizing_const)
626 cout <<
"Further updates will use this precomputed neutralizing "
631 delete precomputed_neutralizing_lf;
633 *precomputed_neutralizing_lf = 0.0;
637 precomputed_neutralizing_lf->
Assemble();
639 return *precomputed_neutralizing_lf;
649 MPI_Comm_rank(pmesh->
GetComm(), &curr_rank);
660 for (
int p = 0;
p < npt; ++
p)
663 MFEM_ASSERT(code[
p] != 2,
"Particle " <<
p <<
" not found.");
666 MFEM_ASSERT((
int)proc[
p] == curr_rank,
667 "Particle " <<
p <<
" found in element owned by rank "
668 << proc[
p] <<
" but current rank is " << curr_rank
670 <<
"You must call redistribute every time before "
671 "updating the density grid function.");
672 const int e = elem[
p];
679 const int ldofs = fe.
GetDof();
693void FieldSolver::UpdatePhiGridFunction(
ParticleSet& particles,
705 MPI_Comm comm = pfes->
GetComm();
706 b = ComputeNeutralizingRHS(pfes, Q, comm);
712 DepositCharge(pfes, Q);
726 solver.SetOperator(*diffusion_matrix);
727 solver.SetTol(1e-12);
728 solver.SetMaxIter(200);
729 solver.SetPrintLevel(0);
732 prec.SetPrintLevel(0);
733 solver.SetPreconditioner(prec);
736 ortho.SetSolver(solver);
737 ortho.Mult(B, Phi_true);
747 grad_interpolator->
Mult(phi_gf, E_gf);
759 const int qorder = std::max(2, 2 * order + 1);
767 real_t field_energy = 0.0;
774 field_energy = 0.5 * EPSILON * E_l2 * E_l2;
A coefficient that is constant across space and time.
Class for domain integration .
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 const Vector & GetReferencePosition() const
Return reference coordinates for each point found by FindPoints.
virtual void Interpolate(const GridFunction &field_in, Vector &field_out)
Interpolation of field values at prescribed reference space positions.
virtual const Array< unsigned int > & GetCode() const
Return code for each point searched by FindPoints: inside element (0), element boundary (1),...
virtual const Array< unsigned int > & GetElem() const
Return element number for each point found by FindPoints.
virtual const Array< unsigned int > & GetProc() const
Return MPI rank on which each point was found by FindPoints.
Abstract class for all finite elements.
virtual void CalcShape(const IntegrationPoint &ip, Vector &shape) const =0
Evaluate the values of all shape functions of a scalar finite element in reference space at the given...
int GetDof() const
Returns the number of degrees of freedom in the finite element.
Arbitrary order H1-conforming (continuous) finite elements.
The BoomerAMG solver in hypre.
Wrapper for hypre's ParCSR matrix class.
MPI_Comm GetComm() const
MPI communicator.
Wrapper for hypre's parallel vector class.
static void Init()
Initialize hypre by calling HYPRE_Init() and set default options. After calling Hypre::Init(),...
Class for integration point with weight.
void Set(const real_t x1, const real_t x2, const real_t x3, const real_t w)
Class for an integration rule - an Array of IntegrationPoint.
const IntegrationRule & Get(int GeomType, int Order)
Returns an integration rule for given GeomType and Order.
void EnsureNodes()
Make sure that the mesh has valid nodes, i.e. its geometry is described by a vector finite element gr...
void Clear()
Clear the contents of the Mesh.
int GetNE() const
Returns number of elements.
int Dimension() const
Dimension of the reference space used within the elements.
int SpaceDimension() const
Dimension of the physical space containing the mesh.
static Mesh MakeCartesian3D(int nx, int ny, int nz, Element::Type type, real_t sx=1.0, real_t sy=1.0, real_t sz=1.0, bool sfc_ordering=true)
Creates a mesh for the parallelepiped [0,sx]x[0,sy]x[0,sz], divided into nx*ny*nz hexahedra if type =...
static Mesh MakePeriodic(const Mesh &orig_mesh, const std::vector< int > &v2v)
Create a periodic mesh by identifying vertices of orig_mesh.
real_t GetElementVolume(int i)
std::vector< int > CreatePeriodicVertexMapping(const std::vector< Vector > &translations, real_t tol=1e-8) const
Creates a mapping v2v from the vertex indices of the mesh such that coincident vertices under the giv...
static Mesh MakeCartesian2D(int nx, int ny, Element::Type type, bool generate_edges=false, real_t sx=1.0, real_t sy=1.0, bool sfc_ordering=true)
Creates mesh for the rectangle [0,sx]x[0,sy], divided into nx*ny quadrilaterals if type = QUADRILATER...
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).
Arbitrary order H(curl)-conforming Nedelec finite elements.
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.
Solver wrapper which orthogonalizes the input and output vector.
Abstract parallel finite element space.
int GetMaxElementOrder() const override
Returns the maximum polynomial order over all elements globally.
void GetElementDofs(int i, Array< int > &dofs, DofTransformation &doftrans) const override
The same as GetElementDofs(), but with a user-provided DofTransformation object.
ParMesh * GetParMesh() const
const FiniteElement * GetFE(int i) const override
Class for parallel grid function.
real_t ComputeL2Error(Coefficient *exsol[], const IntegrationRule *irs[]=NULL, const Array< int > *elems=NULL) const override
Returns ||u_ex - u_h||_L2 in parallel for H1 or L2 elements.
ParFiniteElementSpace * ParFESpace() const
void Distribute(const Vector *tv)
Class for parallel meshes.
ParticleSet initializes and manages data associated with particles.
ParticleVector & Field(int f)
Get a reference to field f 's ParticleVector.
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.
double RealTime()
Return the number of real seconds elapsed since the stopwatch was started.
void Start()
Start the stopwatch. The elapsed time is not cleared.
Vector coefficient that is constant in space and time.
void Neg()
(*this) = -(*this)
void AddElementVector(const Array< int > &dofs, const Vector &elemvect)
Add elements of the elemvect Vector to the entries listed in dofs. Negative dof values cause the -dof...
int Size() const
Returns the size of the vector.
real_t * GetData() const
Return a pointer to the beginning of the Vector data.
void display_banner(ostream &os)
Prints the program's logo to the given output stream.
void VisualizeField(socketstream &sock, const char *vishost, int visport, GridFunction &gf, const char *title, int x, int y, int w, int h, const char *keys, bool vec)
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
std::string to_padded_string(int i, int digits)
Convert an integer to a 0-padded string with the given number of digits.
IntegrationRules IntRules(0, Quadrature1D::GaussLegendre)
A global object with all integration rules (defined in intrules.cpp)
real_t p(const Vector &x, real_t t)
Helper struct to convert a C++ type to an MPI type.