MFEM v4.9.0
Finite element discretization library
Loading...
Searching...
No Matches
maxwell.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// Maxwell Miniapp: Simple Full-Wave Electromagnetic Simulation Code
14// ------------------------------------------------------------------
15//
16// This miniapp solves a simple 3D full-wave electromagnetic problem using the
17// coupled, first-order equations:
18//
19// epsilon dE/dt = Curl 1/mu B - sigma E - J
20// dB/dt = - Curl E
21//
22// The permittivity function is that of the vacuum with an optional dielectric
23// sphere. The permeability function is that of the vacuum with an optional
24// diamagnetic or paramagnetic spherical shell. The optional conductivity
25// function is also a user-defined sphere.
26//
27// The optional current density is a pulse of current in the shape of a cylinder
28// with a time dependence resembling the derivative of a Gaussian distribution.
29//
30// Boundary conditions can be 'natural' meaning zero tangential current,
31// 'Dirichlet' which sets the time-derivative of the tangential components of E,
32// or 'absorbing' (we use a simple Sommerfeld first order absorbing boundary
33// condition).
34//
35// We discretize the electric field with H(Curl) finite elements (Nedelec edge
36// elements) and the magnetic flux with H(Div) finite elements (Raviart-Thomas
37// elements).
38//
39// The symplectic time integration algorithm used below is designed to conserve
40// energy unless lossy materials or absorbing boundary conditions are used.
41// When losses are expected, the algorithm uses an implicit method which
42// includes the loss operators in the left hand side of the linear system.
43//
44// For increased accuracy the time integration order can be set to 2, 3, or 4
45// (the default is 1st order).
46//
47// Compile with: make maxwell
48//
49// Sample runs:
50//
51// Current source in a sphere with absorbing boundary conditions:
52// mpirun -np 4 maxwell -m ../../data/ball-nurbs.mesh -rs 2 -abcs '-1' -dp '-0.3 0.0 0.0 0.3 0.0 0.0 0.1 1 .5 .5'
53//
54// Current source in a metal sphere with dielectric and conducting materials:
55// mpirun -np 4 maxwell -m ../../data/ball-nurbs.mesh -rs 2 -dbcs '-1' -dp '-0.3 0.0 0.0 0.3 0.0 0.0 0.1 1 .5 .5' -cs '0.0 0.0 -0.5 .2 3e6' -ds '0.0 0.0 0.5 .2 10'
56//
57// Current source in a metal box:
58// mpirun -np 4 maxwell -m ../../data/fichera.mesh -rs 3 -ts 0.25 -tf 10 -dbcs '-1' -dp '-0.5 -0.5 0.0 -0.5 -0.5 1.0 0.1 1 .5 1'
59//
60// Current source with a mixture of absorbing and reflecting boundaries:
61// mpirun -np 4 maxwell -m ../../data/fichera.mesh -rs 3 -ts 0.25 -tf 10 -dp '-0.5 -0.5 0.0 -0.5 -0.5 1.0 0.1 1 .5 1' -dbcs '4 8 19 21' -abcs '5 18'
62//
63// By default the sources and fields are all zero:
64// * mpirun -np 4 maxwell
65
66#include "maxwell_solver.hpp"
67#include <fstream>
68#include <iostream>
69
70using namespace std;
71using namespace mfem;
72using namespace mfem::common;
73using namespace mfem::electromagnetics;
74
75// Permittivity Function
76static Vector ds_params_(0); // Center, Radius, and Permittivity
77// of dielectric sphere
79real_t epsilon(const Vector &x) { return dielectric_sphere(x); }
80
81// Permeability Function
82static Vector ms_params_(0); // Center, Inner and Outer Radii, and
83// Permeability of magnetic shell
85real_t muInv(const Vector & x) { return 1.0/magnetic_shell(x); }
86
87// Conductivity Function
88static Vector cs_params_(0); // Center, Radius, and Conductivity
89// of conductive sphere
91real_t sigma(const Vector &x) { return conductive_sphere(x); }
92
93// Current Density Function
94static Vector dp_params_(0); // Axis Start, Axis End, Rod Radius,
95// Total Current of Rod, and Frequency
96void dipole_pulse(const Vector &x, real_t t, Vector &j);
97void j_src(const Vector &x, real_t t, Vector &j) { dipole_pulse(x, t, j); }
98
99// dE/dt Boundary Condition: The following function returns zero but any time
100// dependent function could be used.
101void dEdtBCFunc(const Vector &x, real_t t, Vector &E);
102
103// The following functions return zero but they could be modified to set initial
104// conditions for the electric and magnetic fields
105void EFieldFunc(const Vector &, Vector&);
106void BFieldFunc(const Vector &, Vector&);
107
108// Scale factor between input time units and seconds
109static real_t tScale_ = 1e-9; // Input time in nanosecond
110
111int SnapTimeStep(real_t tmax, real_t dtmax, real_t & dt);
112
113// Prints the program's logo to the given output stream
114void display_banner(ostream & os);
115
116int main(int argc, char *argv[])
117{
118#ifdef MFEM_USE_SINGLE
119 cout << "This miniapp is not supported in single precision.\n\n";
120 return MFEM_SKIP_RETURN_VALUE;
121#endif
122
123 Mpi::Init();
124 Hypre::Init();
125
126 if ( Mpi::Root() ) { display_banner(cout); }
127
128 // Parse command-line options.
129 const char *mesh_file = "../../data/ball-nurbs.mesh";
130 int sOrder = 1;
131 int tOrder = 1;
132 int serial_ref_levels = 0;
133 int parallel_ref_levels = 0;
134 int visport = 19916;
135 bool visualization = true;
136 bool visit = true;
137 real_t dt = 1.0e-12;
138 real_t dtsf = 0.95;
139 real_t ti = 0.0;
140 real_t ts = 1.0;
141 real_t tf = 40.0;
142
143 Array<int> abcs;
144 Array<int> dbcs;
145
146 OptionsParser args(argc, argv);
147 args.AddOption(&mesh_file, "-m", "--mesh",
148 "Mesh file to use.");
149 args.AddOption(&sOrder, "-so", "--spatial-order",
150 "Finite element order (polynomial degree).");
151 args.AddOption(&tOrder, "-to", "--temporal-order",
152 "Time integration order.");
153 args.AddOption(&serial_ref_levels, "-rs", "--serial-ref-levels",
154 "Number of serial refinement levels.");
155 args.AddOption(&parallel_ref_levels, "-rp", "--parallel-ref-levels",
156 "Number of parallel refinement levels.");
157 args.AddOption(&dtsf, "-sf", "--dt-safety-factor",
158 "Used to reduce the time step below the upper bound.");
159 args.AddOption(&ti, "-ti", "--initial-time",
160 "Beginning of time interval to simulate (ns).");
161 args.AddOption(&tf, "-tf", "--final-time",
162 "End of time interval to simulate (ns).");
163 args.AddOption(&ts, "-ts", "--snapshot-time",
164 "Time between snapshots (ns).");
165 args.AddOption(&ds_params_, "-ds", "--dielectric-sphere-params",
166 "Center, Radius, and Permittivity of Dielectric Sphere");
167 args.AddOption(&ms_params_, "-ms", "--magnetic-shell-params",
168 "Center, Inner Radius, Outer Radius, and Permeability "
169 "of Magnetic Shell");
170 args.AddOption(&cs_params_, "-cs", "--conductive-sphere-params",
171 "Center, Radius, and Conductivity of Conductive Sphere");
172 args.AddOption(&dp_params_, "-dp", "--dipole-pulse-params",
173 "Axis End Points, Radius, Amplitude, "
174 "Pulse Center (ns), Pulse Width (ns)");
175 args.AddOption(&abcs, "-abcs", "--absorbing-bc-surf",
176 "Absorbing Boundary Condition Surfaces");
177 args.AddOption(&dbcs, "-dbcs", "--dirichlet-bc-surf",
178 "Dirichlet Boundary Condition Surfaces");
179 args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
180 "--no-visualization",
181 "Enable or disable GLVis visualization.");
182 args.AddOption(&visit, "-visit", "--visit", "-no-visit",
183 "--no-visualization",
184 "Enable or disable VisIt visualization.");
185 args.AddOption(&visport, "-p", "--send-port", "Socket for GLVis.");
186 args.Parse();
187 if (!args.Good())
188 {
189 if (Mpi::Root())
190 {
191 args.PrintUsage(cout);
192 }
193 return 1;
194 }
195 if (Mpi::Root())
196 {
197 args.PrintOptions(cout);
198 }
199
200 // Read the (serial) mesh from the given mesh file on all processors. We can
201 // handle triangular, quadrilateral, tetrahedral, hexahedral, surface and
202 // volume meshes with the same code.
203 Mesh *mesh;
204 ifstream imesh(mesh_file);
205 if (!imesh)
206 {
207 if (Mpi::Root())
208 {
209 cerr << "\nCan not open mesh file: " << mesh_file << '\n' << endl;
210 }
211 return 2;
212 }
213 mesh = new Mesh(imesh, 1, 1);
214 imesh.close();
215
216 // Project a NURBS mesh to a piecewise-quadratic curved mesh
217 if (mesh->NURBSext)
218 {
219 mesh->UniformRefinement();
220 if (serial_ref_levels > 0) { serial_ref_levels--; }
221
222 mesh->SetCurvature(2);
223 }
224
225 // Refine the serial mesh on all processors to increase the resolution. In
226 // this example we do 'ref_levels' of uniform refinement.
227 for (int l = 0; l < serial_ref_levels; l++)
228 {
229 mesh->UniformRefinement();
230 }
231
232 // Define a parallel mesh by a partitioning of the serial mesh. Refine this
233 // mesh further in parallel to increase the resolution. Once the parallel
234 // mesh is defined, the serial mesh can be deleted.
235 ParMesh pmesh(MPI_COMM_WORLD, *mesh);
236 delete mesh;
237
238 // Refine this mesh in parallel to increase the resolution.
239 for (int l = 0; l < parallel_ref_levels; l++)
240 {
241 pmesh.UniformRefinement();
242 }
243
244 // Create the Electromagnetic solver
245 MaxwellSolver Maxwell(pmesh, sOrder,
246 ( ds_params_.Size() > 0 ) ? epsilon : NULL,
247 ( ms_params_.Size() > 0 ) ? muInv : NULL,
248 ( cs_params_.Size() > 0 ) ? sigma : NULL,
249 ( dp_params_.Size() > 0 ) ? j_src : NULL,
250 abcs, dbcs,
251 ( dbcs.Size() > 0 ) ? dEdtBCFunc : NULL
252 );
253
254 // Display the current number of DoFs in each finite element space
255 Maxwell.PrintSizes();
256
257 // Set the initial conditions for both the electric and magnetic fields
260
261 Maxwell.SetInitialEField(EFieldCoef);
262 Maxwell.SetInitialBField(BFieldCoef);
263
264 // Compute the energy of the initial fields
265 real_t energy = Maxwell.GetEnergy();
266 if ( Mpi::Root() )
267 {
268 cout << "Energy(" << ti << "ns): " << energy << "J" << endl;
269 }
270
271 // Approximate the largest stable time step
272 real_t dtmax = Maxwell.GetMaximumTimeStep();
273
274 // Convert times from nanoseconds to seconds
275 ti *= tScale_;
276 tf *= tScale_;
277 ts *= tScale_;
278
279 if ( Mpi::Root() )
280 {
281 cout << "Maximum Time Step: " << dtmax / tScale_ << "ns" << endl;
282 }
283
284 // Round down the time step so that tf-ti is an integer multiple of dt
285 int nsteps = SnapTimeStep(tf-ti, dtsf * dtmax, dt);
286 if ( Mpi::Root() )
287 {
288 cout << "Number of Time Steps: " << nsteps << endl;
289 cout << "Time Step Size: " << dt / tScale_ << "ns" << endl;
290 }
291
292 // Create the ODE solver
293 SIAVSolver siaSolver(tOrder);
294 siaSolver.Init(Maxwell.GetNegCurl(), Maxwell);
295
296
297 // Initialize GLVis visualization
298 if (visualization)
299 {
300 Maxwell.InitializeGLVis();
301 }
302
303 // Initialize VisIt visualization
304 VisItDataCollection visit_dc("Maxwell-Parallel", &pmesh);
305
306 real_t t = ti;
307 Maxwell.SetTime(t);
308
309 if ( visit )
310 {
311 Maxwell.RegisterVisItFields(visit_dc);
312 }
313
314 // Write initial fields to disk for VisIt
315 if ( visit )
316 {
317 Maxwell.WriteVisItFields(0);
318 }
319
320 // Send the initial condition by socket to a GLVis server.
321 if (visualization)
322 {
323 Maxwell.DisplayToGLVis(visport);
324 }
325
326 // The main time evolution loop.
327 int it = 1;
328 while (t < tf)
329 {
330 // Run the simulation until a snapshot is needed
331 siaSolver.Run(Maxwell.GetBField(), Maxwell.GetEField(), t, dt,
332 max(t + dt, ti + ts * it));
333
334 // Approximate the current energy if the fields
335 energy = Maxwell.GetEnergy();
336 if ( Mpi::Root() )
337 {
338 cout << "Energy(" << t/tScale_ << "ns): " << energy << "J" << endl;
339 }
340
341 // Update local DoFs with current true DoFs
342 Maxwell.SyncGridFuncs();
343
344 // Write fields to disk for VisIt
345 if ( visit )
346 {
347 Maxwell.WriteVisItFields(it);
348 }
349
350 // Send the solution by socket to a GLVis server.
351 if (visualization)
352 {
353 Maxwell.DisplayToGLVis(visport);
354 }
355
356 it++;
357 }
358
359 return 0;
360}
361
362// Print the Maxwell ascii logo to the given ostream
363void display_banner(ostream & os)
364{
365 os << " ___ ____ " << endl
366 << " / | / / __ __ " << endl
367 << " / |_/ _ /__ ___ _____ _ __ ____ | | | | " << endl
368 << " / \\__ \\ \\ \\/ /\\ \\/ \\/ // __ \\| | | | "
369 << endl
370 << " / /|_/ // __ \\_> < \\ /\\ ___/| |_| |__ " << endl
371 << "/___/ /_ /(____ /__/\\_ \\ \\/\\_/ \\___ >____/____/ " << endl
372 << " \\/ \\/ \\/ \\/ " << endl
373 << flush;
374}
375
376// A sphere with constant permittivity. The sphere has a radius, center, and
377// permittivity specified on the command line and stored in ds_params_.
379{
380 real_t r2 = 0.0;
381
382 for (int i=0; i<x.Size(); i++)
383 {
384 r2 += (x(i)-ds_params_(i))*(x(i)-ds_params_(i));
385 }
386
387 if ( sqrt(r2) <= ds_params_(x.Size()) )
388 {
389 return ds_params_(x.Size()+1) * epsilon0_;
390 }
391 return epsilon0_;
392}
393
394// A spherical shell with constant permeability. The sphere has inner and outer
395// radii, center, and relative permeability specified on the command line and
396// stored in ms_params_.
398{
399 real_t r2 = 0.0;
400
401 for (int i=0; i<x.Size(); i++)
402 {
403 r2 += (x(i)-ms_params_(i))*(x(i)-ms_params_(i));
404 }
405
406 if ( sqrt(r2) >= ms_params_(x.Size()) &&
407 sqrt(r2) <= ms_params_(x.Size()+1) )
408 {
409 return mu0_*ms_params_(x.Size()+2);
410 }
411 return mu0_;
412}
413
414// A sphere with constant conductivity. The sphere has a radius, center, and
415// conductivity specified on the command line and stored in ls_params_.
417{
418 real_t r2 = 0.0;
419
420 for (int i=0; i<x.Size(); i++)
421 {
422 r2 += (x(i)-cs_params_(i))*(x(i)-cs_params_(i));
423 }
424
425 if ( sqrt(r2) <= cs_params_(x.Size()) )
426 {
427 return cs_params_(x.Size()+1);
428 }
429 return 0.0;
430}
431
432// A cylindrical rod of current density. The rod has two axis end points, a
433// radius, a current amplitude in Amperes, a center time, and a width. All of
434// these parameters are stored in dp_params_.
435void dipole_pulse(const Vector &x, real_t t, Vector &j)
436{
437 MFEM_ASSERT(x.Size() == 3, "current source requires 3D space.");
438
439 j.SetSize(x.Size());
440 j = 0.0;
441
442 Vector v(x.Size()); // Normalized Axis vector
443 Vector xu(x.Size()); // x vector relative to the axis end-point
444
445 xu = x;
446
447 for (int i=0; i<x.Size(); i++)
448 {
449 xu[i] -= dp_params_[i];
450 v[i] = dp_params_[x.Size()+i] - dp_params_[i];
451 }
452
453 real_t h = v.Norml2();
454
455 if ( h == 0.0 )
456 {
457 return;
458 }
459 v /= h;
460
461 real_t r = dp_params_[2*x.Size()+0];
462 real_t a = dp_params_[2*x.Size()+1] * tScale_;
463 real_t b = dp_params_[2*x.Size()+2] * tScale_;
464 real_t c = dp_params_[2*x.Size()+3] * tScale_;
465
466 real_t xv = xu * v;
467
468 // Compute perpendicular vector from axis to x
469 xu.Add(-xv, v);
470
471 real_t xp = xu.Norml2();
472
473 if ( xv >= 0.0 && xv <= h && xp <= r )
474 {
475 j = v;
476 }
477
478 j *= a * (t - b) * exp(-0.5 * pow((t-b)/c, 2.0)) / (c * c);
479}
480
481void
483{
484 E.SetSize(3);
485 E = 0.0;
486}
487
488void
490{
491 B.SetSize(3);
492 B = 0.0;
493}
494
495void
497{
498 dE.SetSize(3);
499 dE = 0.0;
500}
501
502int
504{
505 real_t dsteps = tmax/dtmax;
506
507 int nsteps = static_cast<int>(pow(10,(int)ceil(log10(dsteps))));
508
509 for (int i=1; i<=5; i++)
510 {
511 int a = (int)ceil(log10(dsteps/pow(5.0,i)));
512 int nstepsi = (int)pow(5,i)*max(1,(int)pow(10,a));
513
514 nsteps = min(nsteps,nstepsi);
515 }
516
517 dt = tmax / nsteps;
518
519 return nsteps;
520}
int Size() const
Return the logical size of the array.
Definition array.hpp:166
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
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 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.
Class for parallel meshes.
Definition pmesh.hpp:34
virtual void Init(Operator &P, TimeDependentOperator &F)
Definition ode.cpp:971
virtual void Run(Vector &q, Vector &p, real_t &t, real_t &dt, real_t tf)
Definition ode.hpp:675
Variable order Symplectic Integration Algorithm (orders 1-4)
Definition ode.hpp:708
virtual void SetTime(const real_t t_)
Set the current time.
Definition operator.hpp:406
A general vector function coefficient.
Vector data type.
Definition vector.hpp:82
real_t Norml2() const
Returns the l2 norm of the vector.
Definition vector.cpp:968
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 SetInitialEField(VectorCoefficient &EFieldCoef)
void RegisterVisItFields(VisItDataCollection &visit_dc)
void SetInitialBField(VectorCoefficient &BFieldCoef)
real_t sigma(const Vector &x)
Definition maxwell.cpp:91
void dipole_pulse(const Vector &x, real_t t, Vector &j)
Definition maxwell.cpp:435
real_t magnetic_shell(const Vector &)
Definition maxwell.cpp:397
void EFieldFunc(const Vector &, Vector &)
Definition maxwell.cpp:482
real_t muInv(const Vector &x)
Definition maxwell.cpp:85
int SnapTimeStep(real_t tmax, real_t dtmax, real_t &dt)
Definition maxwell.cpp:503
void j_src(const Vector &x, real_t t, Vector &j)
Definition maxwell.cpp:97
void dEdtBCFunc(const Vector &x, real_t t, Vector &E)
Definition maxwell.cpp:496
real_t dielectric_sphere(const Vector &)
Definition maxwell.cpp:378
void display_banner(ostream &os)
Definition maxwell.cpp:363
void BFieldFunc(const Vector &, Vector &)
Definition maxwell.cpp:489
real_t conductive_sphere(const Vector &)
Definition maxwell.cpp:416
real_t epsilon
Definition ex25.cpp:141
int main()
real_t b
Definition lissajous.cpp:42
real_t a
Definition lissajous.cpp:41
float real_t
Definition config.hpp:46
MFEM_HOST_DEVICE Complex exp(const Complex &q)