MFEM v4.9.0
Finite element discretization library
Loading...
Searching...
No Matches
volta.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
2// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3// LICENSE and NOTICE for details. LLNL-CODE-806117.
4//
5// This file is part of the MFEM library. For more information and source code
6// availability visit https://mfem.org.
7//
8// MFEM is free software; you can redistribute it and/or modify it under the
9// terms of the BSD-3 license. We welcome feedback and contributions, see file
10// CONTRIBUTING.md for details.
11//
12// -----------------------------------------------------
13// Volta Miniapp: Simple Electrostatics Simulation Code
14// -----------------------------------------------------
15//
16// This miniapp solves a simple 2D or 3D electrostatic problem.
17//
18// Div eps Grad Phi = rho
19//
20// The permittivity function is that of the vacuum with an optional dielectric
21// sphere. The charge density is either zero or a user defined sphere of charge.
22//
23// Boundary conditions for the electric potential consist of a user defined
24// piecewise constant potential or a potential leading to a user selected
25// uniform electric field.
26//
27// We discretize the electric potential with H1 finite elements. The electric
28// field E is discretized with Nedelec finite elements.
29//
30// Compile with: make volta
31//
32// Sample runs:
33//
34// Three point charges within a large metal enclosure:
35// mpirun -np 4 volta -m ../../data/inline-quad.mesh
36// -pc '0.5 0.42 20 0.5 0.5 -12 0.5 0.545 15'
37// -dbcs '1 2 3 4' -dbcv '0 0 0 0'
38//
39// A cylinder at constant voltage in a square, grounded metal pipe:
40// mpirun -np 4 volta -m ../../data/square-disc.mesh
41// -dbcs '1 2 3 4 5 6 7 8' -dbcv '0 0 0 0 1 1 1 1'
42//
43// A cylinder with a constant surface charge density in a square,
44// grounded metal pipe:
45// mpirun -np 4 volta -m ../../data/square-disc.mesh
46// -nbcs '5 6 7 8' -nbcv '5e-11 5e-11 5e-11 5e-11'
47// -dbcs '1 2 3 4'
48//
49// A cylindrical voltaic pile within a grounded metal sphere:
50// mpirun -np 4 volta -dbcs 1 -vp '0 -0.5 0 0 0.5 0 0.2 1'
51//
52// A charged sphere, off-center, within a grounded metal sphere:
53// mpirun -np 4 volta -dbcs 1 -cs '0.0 0.5 0.0 0.2 2.0e-11'
54//
55// A dielectric sphere suspended in a uniform electric field:
56// mpirun -np 4 volta -dbcs 1 -dbcg -ds '0.0 0.0 0.0 0.2 8.0'
57//
58// An example using piecewise constant permittivity values
59// mpirun -np 4 volta -m llnl.mesh -dbcs '4' -dbcv '0'
60// -cs '8.5 8.5 17 1.57' -pwe '1 1 1 0.001'
61//
62// By default the sources and fields are all zero:
63// mpirun -np 4 volta
64
65#include "volta_solver.hpp"
66#include <fstream>
67#include <iostream>
68
69using namespace std;
70using namespace mfem;
71using namespace mfem::electromagnetics;
72
73// Permittivity Functions
75
76static Vector pw_eps_(0); // Piecewise permittivity values
77static Vector ds_params_(0); // Center, Radius, and Permittivity
78// of dielectric sphere
80
81// Charge Density Function
82static Vector cs_params_(0); // Center, Radius, and Total Charge
83// of charged sphere
85
86// Point Charges
87static Vector pc_params_(0); // Point charge locations and charges
88
89// Polarization
90static Vector vp_params_(0); // Axis Start, Axis End, Cylinder Radius,
91// and Polarization Magnitude
92void voltaic_pile(const Vector &, Vector &);
93
94// Phi Boundary Condition
95static Vector e_uniform_(0);
97
98// Prints the program's logo to the given output stream
99void display_banner(ostream & os);
100
101int main(int argc, char *argv[])
102{
103 Mpi::Init(argc, argv);
104 Hypre::Init();
105
106 if ( Mpi::Root() ) { display_banner(cout); }
107
108 // Parse command-line options.
109 const char *mesh_file = "../../data/ball-nurbs.mesh";
110 int order = 1;
111 int maxit = 100;
112 int serial_ref_levels = 0;
113 int parallel_ref_levels = 0;
114 int visport = 19916;
115 bool visualization = true;
116 bool visit = true;
117
118 Array<int> dbcs;
119 Array<int> nbcs;
120
121 Vector dbcv;
122 Vector nbcv;
123
124 bool dbcg = false;
125
126 OptionsParser args(argc, argv);
127 args.AddOption(&mesh_file, "-m", "--mesh",
128 "Mesh file to use.");
129 args.AddOption(&order, "-o", "--order",
130 "Finite element order (polynomial degree).");
131 args.AddOption(&serial_ref_levels, "-rs", "--serial-ref-levels",
132 "Number of serial refinement levels.");
133 args.AddOption(&parallel_ref_levels, "-rp", "--parallel-ref-levels",
134 "Number of parallel refinement levels.");
135 args.AddOption(&e_uniform_, "-uebc", "--uniform-e-bc",
136 "Specify if the three components of the constant "
137 "electric field");
138 args.AddOption(&pw_eps_, "-pwe", "--piecewise-eps",
139 "Piecewise values of Permittivity");
140 args.AddOption(&ds_params_, "-ds", "--dielectric-sphere-params",
141 "Center, Radius, and Permittivity of Dielectric Sphere");
142 args.AddOption(&cs_params_, "-cs", "--charged-sphere-params",
143 "Center, Radius, and Total Charge of Charged Sphere");
144 args.AddOption(&pc_params_, "-pc", "--point-charge-params",
145 "Charges and locations of Point Charges");
146 args.AddOption(&vp_params_, "-vp", "--voltaic-pile-params",
147 "Axis End Points, Radius, and "
148 "Polarization of Cylindrical Voltaic Pile");
149 args.AddOption(&dbcs, "-dbcs", "--dirichlet-bc-surf",
150 "Dirichlet Boundary Condition Surfaces");
151 args.AddOption(&dbcv, "-dbcv", "--dirichlet-bc-vals",
152 "Dirichlet Boundary Condition Values");
153 args.AddOption(&dbcg, "-dbcg", "--dirichlet-bc-gradient",
154 "-no-dbcg", "--no-dirichlet-bc-gradient",
155 "Dirichlet Boundary Condition Gradient (phi = -z)");
156 args.AddOption(&nbcs, "-nbcs", "--neumann-bc-surf",
157 "Neumann Boundary Condition Surfaces");
158 args.AddOption(&nbcv, "-nbcv", "--neumann-bc-vals",
159 "Neumann Boundary Condition Values");
160 args.AddOption(&maxit, "-maxit", "--max-amr-iterations",
161 "Max number of iterations in the main AMR loop.");
162 args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
163 "--no-visualization",
164 "Enable or disable GLVis visualization.");
165 args.AddOption(&visit, "-visit", "--visit", "-no-visit", "--no-visit",
166 "Enable or disable VisIt visualization.");
167 args.AddOption(&visport, "-p", "--send-port", "Socket for GLVis.");
168 args.Parse();
169 if (!args.Good())
170 {
171 if (Mpi::Root())
172 {
173 args.PrintUsage(cout);
174 }
175 return 1;
176 }
177 if (Mpi::Root())
178 {
179 args.PrintOptions(cout);
180 }
181
182 // Read the (serial) mesh from the given mesh file on all processors. We
183 // can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
184 // and volume meshes with the same code.
185 Mesh *mesh = new Mesh(mesh_file, 1, 1);
186 int sdim = mesh->SpaceDimension();
187
188 if (Mpi::Root())
189 {
190 cout << "Starting initialization." << endl;
191 }
192
193 // Project a NURBS mesh to a piecewise-quadratic curved mesh
194 if (mesh->NURBSext)
195 {
196 mesh->UniformRefinement();
197 if (serial_ref_levels > 0) { serial_ref_levels--; }
198
199 mesh->SetCurvature(2);
200 }
201
202 // Ensure that quad and hex meshes are treated as non-conforming.
203 mesh->EnsureNCMesh();
204
205 // Refine the serial mesh on all processors to increase the resolution. In
206 // this example we do 'ref_levels' of uniform refinement. NURBS meshes are
207 // refined at least twice, as they are typically coarse.
208 for (int l = 0; l < serial_ref_levels; l++)
209 {
210 mesh->UniformRefinement();
211 }
212
213 // Define a parallel mesh by a partitioning of the serial mesh. Refine
214 // this mesh further in parallel to increase the resolution. Once the
215 // parallel mesh is defined, the serial mesh can be deleted.
216 ParMesh pmesh(MPI_COMM_WORLD, *mesh);
217 delete mesh;
218
219 // Refine this mesh in parallel to increase the resolution.
220 int par_ref_levels = parallel_ref_levels;
221 for (int l = 0; l < par_ref_levels; l++)
222 {
223 pmesh.UniformRefinement();
224 }
225 // Make sure tet-only meshes are marked for local refinement.
226 pmesh.Finalize(true);
227
228 // If the gradient bc was selected but the E field was not specified
229 // set a default vector value.
230 if ( dbcg && e_uniform_.Size() != sdim )
231 {
232 e_uniform_.SetSize(sdim);
233 e_uniform_ = 0.0;
234 e_uniform_(sdim-1) = 1.0;
235 }
236
237 // If values for Dirichlet BCs were not set assume they are zero
238 if ( dbcv.Size() < dbcs.Size() && !dbcg )
239 {
240 dbcv.SetSize(dbcs.Size());
241 dbcv = 0.0;
242 }
243
244 // If values for Neumann BCs were not set assume they are zero
245 if ( nbcv.Size() < nbcs.Size() )
246 {
247 nbcv.SetSize(nbcs.Size());
248 nbcv = 0.0;
249 }
250
251 // Create a coefficient describing the dielectric permittivity
253
254 // Create the Electrostatic solver
255 VoltaSolver Volta(pmesh, order, dbcs, dbcv, nbcs, nbcv, *epsCoef,
256 ( e_uniform_.Size() > 0 ) ? phi_bc_uniform : NULL,
257 ( cs_params_.Size() > 0 ) ? charged_sphere : NULL,
258 ( vp_params_.Size() > 0 ) ? voltaic_pile : NULL,
259 pc_params_);
260
261 // Initialize GLVis visualization
262 if (visualization)
263 {
264 Volta.InitializeGLVis();
265 }
266
267 // Initialize VisIt visualization
268 VisItDataCollection visit_dc("Volta-AMR-Parallel", &pmesh);
270
271 if ( visit )
272 {
273 Volta.RegisterVisItFields(visit_dc);
274 }
275 if (Mpi::Root()) { cout << "Initialization done." << endl; }
276
277 // The main AMR loop. In each iteration we solve the problem on the current
278 // mesh, visualize the solution, estimate the error on all elements, refine
279 // the worst elements and update all objects to work with the new mesh. We
280 // refine until the maximum number of dofs in the nodal finite element space
281 // reaches 10 million.
282 const int max_dofs = 10000000;
283 for (int it = 1; it <= maxit; it++)
284 {
285 if (Mpi::Root())
286 {
287 cout << "\nAMR Iteration " << it << endl;
288 }
289
290 // Display the current number of DoFs in each finite element space
291 Volta.PrintSizes();
292
293 // Assemble all forms
294 Volta.Assemble();
295
296 // Solve the system and compute any auxiliary fields
297 Volta.Solve();
298
299 // Determine the current size of the linear system
300 int prob_size = Volta.GetProblemSize();
301
302 // Write fields to disk for VisIt
303 if ( visit )
304 {
305 Volta.WriteVisItFields(it);
306 }
307
308 // Send the solution by socket to a GLVis server.
309 if (visualization)
310 {
311 Volta.DisplayToGLVis(visport);
312 }
313
314 if (Mpi::Root())
315 {
316 cout << "AMR iteration " << it << " complete." << endl;
317 }
318
319 // Check stopping criteria
320 if (prob_size > max_dofs)
321 {
322 if (Mpi::Root())
323 {
324 cout << "Reached maximum number of dofs, exiting..." << endl;
325 }
326 break;
327 }
328 if (it == maxit)
329 {
330 break;
331 }
332
333 // Wait for user input. Ask every 10th iteration.
334 char c = 'c';
335 if (Mpi::Root() && (it % 10 == 0))
336 {
337 cout << "press (q)uit or (c)ontinue --> " << flush;
338 cin >> c;
339 }
340 MPI_Bcast(&c, 1, MPI_CHAR, 0, MPI_COMM_WORLD);
341
342 if (c != 'c')
343 {
344 break;
345 }
346
347 // Estimate element errors using the Zienkiewicz-Zhu error estimator.
348 Vector errors(pmesh.GetNE());
349 Volta.GetErrorEstimates(errors);
350
351 real_t local_max_err = errors.Max();
352 real_t global_max_err;
353 MPI_Allreduce(&local_max_err, &global_max_err, 1,
354 MPITypeMap<real_t>::mpi_type, MPI_MAX, pmesh.GetComm());
355
356 // Refine the elements whose error is larger than a fraction of the
357 // maximum element error.
358 const real_t frac = 0.7;
359 real_t threshold = frac * global_max_err;
360 if (Mpi::Root()) { cout << "Refining ..." << endl; }
361 pmesh.RefineByError(errors, threshold);
362
363 // Update the electrostatic solver to reflect the new state of the mesh.
364 Volta.Update();
365
366 if (pmesh.Nonconforming() && Mpi::WorldSize() > 1)
367 {
368 if (Mpi::Root()) { cout << "Rebalancing ..." << endl; }
369 pmesh.Rebalance();
370
371 // Update again after rebalancing
372 Volta.Update();
373 }
374 }
375
376 delete epsCoef;
377
378 return 0;
379}
380
381// Print the Volta ascii logo to the given ostream
382void display_banner(ostream & os)
383{
384 os << " ____ ____ __ __ " << endl
385 << " \\ \\ / /___ | |_/ |______ " << endl
386 << " \\ Y / _ \\| |\\ __\\__ \\ " << endl
387 << " \\ ( <_> ) |_| | / __ \\_ " << endl
388 << " \\___/ \\____/|____/__| (____ / " << endl
389 << " \\/ " << endl << flush;
390}
391
392// The Permittivity is a required coefficient which may be defined in
393// various ways so we'll determine the appropriate coefficient type here.
396{
397 Coefficient * coef = NULL;
398
399 if ( ds_params_.Size() > 0 )
400 {
402 }
403 else if ( pw_eps_.Size() > 0 )
404 {
405 coef = new PWConstCoefficient(pw_eps_);
406 }
407 else
408 {
409 coef = new ConstantCoefficient(epsilon0_);
410 }
411
412 return coef;
413}
414
415// A sphere with constant permittivity. The sphere has a radius,
416// center, and permittivity specified on the command line and stored
417// in ds_params_.
419{
420 real_t r2 = 0.0;
421
422 for (int i=0; i<x.Size(); i++)
423 {
424 r2 += (x(i)-ds_params_(i))*(x(i)-ds_params_(i));
425 }
426
427 if ( sqrt(r2) <= ds_params_(x.Size()) )
428 {
429 return ds_params_(x.Size()+1) * epsilon0_;
430 }
431 return epsilon0_;
432}
433
434// A sphere with constant charge density. The sphere has a radius,
435// center, and total charge specified on the command line and stored
436// in cs_params_.
438{
439 real_t r2 = 0.0;
440 real_t rho = 0.0;
441
442 if ( cs_params_(x.Size()) > 0.0 )
443 {
444 switch ( x.Size() )
445 {
446 case 2:
447 rho = cs_params_(x.Size()+1) /
448 (M_PI * pow(cs_params_(x.Size()), 2));
449 break;
450 case 3:
451 rho = 0.75 * cs_params_(x.Size()+1) /
452 (M_PI * pow(cs_params_(x.Size()), 3));
453 break;
454 default:
455 rho = 0.0;
456 }
457 }
458
459 for (int i=0; i<x.Size(); i++)
460 {
461 r2 += (x(i) - cs_params_(i)) * (x(i) - cs_params_(i));
462 }
463
464 if ( sqrt(r2) <= cs_params_(x.Size()) )
465 {
466 return rho;
467 }
468 return 0.0;
469}
470
471// A Cylindrical Rod of constant polarization. The cylinder has two
472// axis end points, a radius, and a constant electric polarization oriented
473// along the axis.
474void voltaic_pile(const Vector &x, Vector &p)
475{
476 p.SetSize(x.Size());
477 p = 0.0;
478
479 Vector a(x.Size()); // Normalized Axis vector
480 Vector xu(x.Size()); // x vector relative to the axis end-point
481
482 xu = x;
483
484 for (int i=0; i<x.Size(); i++)
485 {
486 xu[i] -= vp_params_[i];
487 a[i] = vp_params_[x.Size()+i] - vp_params_[i];
488 }
489
490 real_t h = a.Norml2();
491
492 if ( h == 0.0 )
493 {
494 return;
495 }
496
497 real_t r = vp_params_[2 * x.Size()];
498 real_t xa = xu * a;
499
500 if ( h > 0.0 )
501 {
502 xu.Add(-xa / (h * h), a);
503 }
504
505 real_t xp = xu.Norml2();
506
507 if ( xa >= 0.0 && xa <= h*h && xp <= r )
508 {
509 p.Add(vp_params_[2 * x.Size() + 1] / h, a);
510 }
511}
512
513// To produce a uniform electric field the potential can be set
514// to (- Ex x - Ey y - Ez z).
516{
517 real_t phi = 0.0;
518
519 for (int i=0; i<x.Size(); i++)
520 {
521 phi -= x(i) * e_uniform_(i);
522 }
523
524 return phi;
525}
int Size() const
Return the logical size of the array.
Definition array.hpp:166
Base class Coefficients that optionally depend on space and time. These are used by the BilinearFormI...
A coefficient that is constant across space and time.
virtual void SetFormat(int fmt)
Set the desired output mesh and data format.
A general function coefficient.
static void Init()
Initialize hypre by calling HYPRE_Init() and set default options. After calling Hypre::Init(),...
Definition hypre.cpp:33
Mesh data type.
Definition mesh.hpp:65
NURBSExtension * NURBSext
Optional NURBS mesh extension.
Definition mesh.hpp:312
bool Nonconforming() const
Definition mesh.hpp:2499
int GetNE() const
Returns number of elements.
Definition mesh.hpp:1377
int SpaceDimension() const
Dimension of the physical space containing the mesh.
Definition mesh.hpp:1309
bool RefineByError(const Array< real_t > &elem_error, real_t threshold, int nonconforming=-1, int nc_limit=0)
Definition mesh.cpp:11362
void EnsureNCMesh(bool simplices_nonconforming=false)
Definition mesh.cpp:11293
virtual void SetCurvature(int order, bool discont=false, int space_dim=-1, int ordering=1)
Set the curvature of the mesh nodes using the given polynomial degree.
Definition mesh.cpp:6799
void UniformRefinement(int i, const DSTable &, int *, int *, int *)
Definition mesh.cpp:11637
static bool Root()
Return true if the rank in MPI_COMM_WORLD is zero.
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...
Definition optparser.hpp:82
bool Good() const
Return true if the command line options were parsed successfully.
A piecewise constant coefficient with the constants keyed off the element attribute numbers.
Class for parallel meshes.
Definition pmesh.hpp:34
MPI_Comm GetComm() const
Definition pmesh.hpp:405
void Rebalance()
Definition pmesh.cpp:4027
void Finalize(bool refine=false, bool fix_orientation=false) override
Finalize the construction of a general Mesh.
Definition pmesh.cpp:1528
Vector data type.
Definition vector.hpp:82
real_t Norml2() const
Returns the l2 norm of the vector.
Definition vector.cpp:968
real_t Max() const
Returns the maximal element of the vector.
Definition vector.cpp:1200
int Size() const
Returns the size of the vector.
Definition vector.hpp:234
void SetSize(int s)
Resize the vector to size s.
Definition vector.hpp:584
Vector & Add(const real_t a, const Vector &Va)
(*this) += a * Va
Definition vector.cpp:326
Data collection with VisIt I/O routines.
void RegisterVisItFields(VisItDataCollection &visit_dc)
void DisplayToGLVis(int visport=19916)
int main()
real_t a
Definition lissajous.cpp:41
float real_t
Definition config.hpp:46
real_t p(const Vector &x, real_t t)
Helper struct to convert a C++ type to an MPI type.
Coefficient * SetupPermittivityCoefficient()
Definition volta.cpp:395
real_t phi_bc_uniform(const Vector &)
Definition volta.cpp:515
real_t dielectric_sphere(const Vector &)
Definition volta.cpp:418
real_t charged_sphere(const Vector &)
Definition volta.cpp:437
void display_banner(ostream &os)
Definition volta.cpp:382
void voltaic_pile(const Vector &, Vector &)
Definition volta.cpp:474