MFEM v4.8.0
Finite element discretization library
Loading...
Searching...
No Matches
joule.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// Joule Miniapp: Transient Magnetics and Joule Heating
14// -----------------------------------------------------
15//
16// This miniapp solves a time dependent eddy current problem, resulting in Joule
17// heating.
18//
19// This version has electrostatic potential, Phi, which is a source term in the
20// EM diffusion equation. The potential itself is driven by essential BC's
21//
22// Div sigma Grad Phi = 0
23// sigma E = Curl B/mu - sigma grad Phi
24// dB/dt = - Curl E
25// F = -k Grad T
26// c dT/dt = -Div(F) + sigma E.E,
27//
28// where B is the magnetic flux, E is the electric field, T is the temperature,
29// F is the thermal flux, sigma is electrical conductivity, mu is the magnetic
30// permeability, and alpha is the thermal diffusivity. The geometry of the
31// domain is assumed to be as follows:
32//
33// boundary attribute 3
34// +---------------------+
35// boundary --->| | boundary
36// attribute 1 | | attribute 2
37// (driven) +---------------------+
38//
39// The voltage BC condition is essential BC on attribute 1 (front) and 2 (rear)
40// given by function p_bc() at bottom of this file.
41//
42// The E-field boundary condition specifies the essential BC (n cross E) on
43// attribute 1 (front) and 2 (rear) given by function edot_bc at bottom of this
44// file. The E-field can be set on attribute 3 also.
45//
46// The thermal boundary condition for the flux F is the natural BC on attribute 1
47// (front) and 2 (rear). This means that dT/dt = 0 on the boundaries, and the
48// initial T = 0.
49//
50// See Section 3 for how the material propertied are assigned to mesh
51// attributes, this needs to be changed for different applications.
52//
53// See Section 5 for how the boundary conditions are assigned to mesh
54// attributes, this needs to be changed for different applications.
55//
56// This code supports a simple version of AMR, all elements containing material
57// attribute 1 are (optionally) refined.
58//
59// Compile with: make joule
60//
61// Sample runs:
62//
63// mpirun -np 8 joule -m cylinder-hex.mesh -p rod
64// mpirun -np 8 joule -m cylinder-tet.mesh -sc 1 -amr 1 -p rod
65// mpirun -np 8 joule -m cylinder-hex-q2.gen -s 22 -dt 0.1 -tf 240.0 -p rod
66//
67// Options:
68//
69// -m [string] the mesh file name
70// -o [int] the order of the basis
71// -rs [int] number of times to serially refine the mesh
72// -rp [int] number of times to refine the mesh in parallel
73// -s [int] time integrator 1=Backward Euler, 2=SDIRK2, 3=SDIRK3,
74// 22=Midpoint, 23=SDIRK23, 34=SDIRK34
75// -tf [double] the final time
76// -dt [double] time step
77// -mu [double] the magnetic permeability
78// -cnd [double] the electrical conductivity
79// -f [double] the frequency of the applied EM BC
80// -vis [int] GLVis -vis = true -no-vis = false
81// -vs [int] visualization step
82// -k [string] base file name for output file
83// -print [int] print solution (gridfunctions) to disk 0 = no, 1 = yes
84// -amr [int] 0 = no amr, 1 = amr
85// -sc [int] 0 = no static condensation, 1 = use static condensation
86// -p [string] specify the problem to run, "rod", "coil", or "test"
87//
88//
89// NOTE: Example meshes for this miniapp are the included cylinder/rod meshes:
90// cylinder-hex.mesh, cylinder-tet.mesh, cylinder-hex-q2.gen,
91// cylinder-tet-q2.gen, as well as the coil.gen mesh which can be
92// downloaded from github.com/mfem/data (its size is 21MB). Note that the
93// meshes with the "gen" extension require MFEM to be built with NetCDF.
94//
95// NOTE: This code is set up to solve two example problems, 1) a straight metal
96// rod surrounded by air, 2) a metal rod surrounded by a metal coil all
97// surrounded by air. To specify problem (1) use the command line options
98// "-p rod -m cylinder-hex-q2.gen", to specify problem (2) use the
99// command line options "-p coil -m coil.gen". Problem (1) has two
100// materials and problem (2) has three materials, and the BC's are
101// different.
102//
103// NOTE: We write out, optionally, grid functions for P, E, B, W, F, and
104// T. These can be visualized using "glvis -np 4 -m mesh.mesh -g E",
105// assuming we used 4 processors.
106
107#include "joule_solver.hpp"
108#include <memory>
109#include <iostream>
110#include <fstream>
111
112using namespace std;
113using namespace mfem;
114using namespace mfem::common;
115using namespace mfem::electromagnetics;
116
117void display_banner(ostream & os);
118
119static real_t mj_ = 0.0;
120static real_t sj_ = 0.0;
121static real_t wj_ = 0.0;
122
123// Initialize variables used in joule_solver.cpp
126
127int main(int argc, char *argv[])
128{
129 // 1. Initialize MPI and HYPRE.
130 Mpi::Init(argc, argv);
131 int myid = Mpi::WorldRank();
132 Hypre::Init();
133
134 // print the cool banner
135 if (Mpi::Root()) { display_banner(cout); }
136
137 // 2. Parse command-line options.
138 const char *mesh_file = "cylinder-hex.mesh";
139 int ser_ref_levels = 0;
140 int par_ref_levels = 0;
141 int order = 2;
142 int ode_solver_type = 1;
143 real_t t_final = 100.0;
144 real_t dt = 0.5;
145 real_t mu = 1.0;
146 real_t sigma = 2.0*M_PI*10;
147 real_t Tcapacity = 1.0;
148 real_t Tconductivity = 0.01;
149 real_t freq = 1.0/60.0;
150 bool visualization = true;
151 bool visit = true;
152 int vis_steps = 1;
153 int gfprint = 0;
154 const char *basename = "Joule";
155 int amr = 0;
156 int debug = 0;
157 int visport = 19916;
158 const char *problem = "rod";
159
160 OptionsParser args(argc, argv);
161 args.AddOption(&mesh_file, "-m", "--mesh",
162 "Mesh file to use.");
163 args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
164 "Number of times to refine the mesh uniformly in serial.");
165 args.AddOption(&par_ref_levels, "-rp", "--refine-parallel",
166 "Number of times to refine the mesh uniformly in parallel.");
167 args.AddOption(&order, "-o", "--order",
168 "Order (degree) of the finite elements.");
169 args.AddOption(&ode_solver_type, "-s", "--ode-solver",
170 "ODE solver: 1 - Backward Euler, 2 - SDIRK2, 3 - SDIRK3\n\t."
171 "\t 22 - Mid-Point, 23 - SDIRK23, 34 - SDIRK34.");
172 args.AddOption(&t_final, "-tf", "--t-final",
173 "Final time; start time is 0.");
174 args.AddOption(&dt, "-dt", "--time-step",
175 "Time step.");
176 args.AddOption(&mu, "-mu", "--permeability",
177 "Magnetic permeability coefficient.");
178 args.AddOption(&sigma, "-cnd", "--sigma",
179 "Conductivity coefficient.");
180 args.AddOption(&freq, "-f", "--frequency",
181 "Frequency of oscillation.");
182 args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
183 "--no-visualization",
184 "Enable or disable GLVis visualization.");
185 args.AddOption(&visit, "-visit", "--visit", "-no-visit", "--no-visit",
186 "Enable or disable VisIt visualization.");
187 args.AddOption(&vis_steps, "-vs", "--visualization-steps",
188 "Visualize every n-th timestep.");
189 args.AddOption(&basename, "-k", "--outputfilename",
190 "Name of the visit dump files");
191 args.AddOption(&gfprint, "-print", "--print",
192 "Print results (grid functions) to disk.");
193 args.AddOption(&amr, "-amr", "--amr",
194 "Enable AMR");
195 args.AddOption(&STATIC_COND, "-sc", "--static-condensation",
196 "Enable static condensation");
197 args.AddOption(&debug, "-debug", "--debug",
198 "Print matrices and vectors to disk");
199 args.AddOption(&SOLVER_PRINT_LEVEL, "-hl", "--hypre-print-level",
200 "Hypre print level");
201 args.AddOption(&problem, "-p", "--problem",
202 "Name of problem to run");
203 args.AddOption(&visport, "-p", "--send-port", "Socket for GLVis.");
204 args.Parse();
205 if (!args.Good())
206 {
207 if (Mpi::Root())
208 {
209 args.PrintUsage(cout);
210 }
211 return 1;
212 }
213 if (Mpi::Root())
214 {
215 args.PrintOptions(cout);
216 }
217
218 mj_ = mu;
219 sj_ = sigma;
220 wj_ = 2.0*M_PI*freq;
221
222 if (Mpi::Root())
223 {
224 cout << "\nSkin depth sqrt(2.0/(wj*mj*sj)) = " << sqrt(2.0/(wj_*mj_*sj_))
225 << "\nSkin depth sqrt(2.0*dt/(mj*sj)) = " << sqrt(2.0*dt/(mj_*sj_))
226 << endl;
227 }
228
229 // 3. Here material properties are assigned to mesh attributes. This code is
230 // not general, it is assumed the mesh has 3 regions each with a different
231 // integer attribute: 1, 2 or 3.
232 //
233 // The coil problem has three regions: 1) coil, 2) air, 3) the rod.
234 // The rod problem has two regions: 1) rod, 2) air.
235 //
236 // We can use the same material maps for both problems.
237
238 std::map<int, real_t> sigmaMap, InvTcondMap, TcapMap, InvTcapMap;
239 real_t sigmaAir = 0.0; // init to suppress gcc warning
240 real_t TcondAir;
241 real_t TcapAir;
242 if (strcmp(problem,"rod")==0 || strcmp(problem,"coil")==0)
243 {
244 sigmaAir = 1.0e-6 * sigma;
245 TcondAir = 1.0e6 * Tconductivity;
246 TcapAir = 1.0 * Tcapacity;
247 }
248 else
249 {
250 cerr << "Problem " << problem << " not recognized\n";
251 mfem_error();
252 }
253
254 if (strcmp(problem,"rod")==0 || strcmp(problem,"coil")==0)
255 {
256
257 sigmaMap.insert(pair<int, real_t>(1, sigma));
258 sigmaMap.insert(pair<int, real_t>(2, sigmaAir));
259 sigmaMap.insert(pair<int, real_t>(3, sigmaAir));
260
261 InvTcondMap.insert(pair<int, real_t>(1, 1.0/Tconductivity));
262 InvTcondMap.insert(pair<int, real_t>(2, 1.0/TcondAir));
263 InvTcondMap.insert(pair<int, real_t>(3, 1.0/TcondAir));
264
265 TcapMap.insert(pair<int, real_t>(1, Tcapacity));
266 TcapMap.insert(pair<int, real_t>(2, TcapAir));
267 TcapMap.insert(pair<int, real_t>(3, TcapAir));
268
269 InvTcapMap.insert(pair<int, real_t>(1, 1.0/Tcapacity));
270 InvTcapMap.insert(pair<int, real_t>(2, 1.0/TcapAir));
271 InvTcapMap.insert(pair<int, real_t>(3, 1.0/TcapAir));
272 }
273 else
274 {
275 cerr << "Problem " << problem << " not recognized\n";
276 mfem_error();
277 }
278
279 // 4. Read the serial mesh from the given mesh file on all processors. We can
280 // handle triangular, quadrilateral, tetrahedral and hexahedral meshes
281 // with the same code.
282 Mesh *mesh;
283 mesh = new Mesh(mesh_file, 1, 1);
284 int dim = mesh->Dimension();
285
286 // 5. Assign the boundary conditions
287 Array<int> ess_bdr(mesh->bdr_attributes.Max());
288 Array<int> thermal_ess_bdr(mesh->bdr_attributes.Max());
289 Array<int> poisson_ess_bdr(mesh->bdr_attributes.Max());
290 if (strcmp(problem,"coil")==0)
291 {
292 // BEGIN CODE FOR THE COIL PROBLEM
293 // For the coil in a box problem we have surfaces 1) coil end (+),
294 // 2) coil end (-), 3) five sides of box, 4) side of box with coil BC
295
296 ess_bdr = 0;
297 ess_bdr[0] = 1; // boundary attribute 4 (index 3) is fixed
298 ess_bdr[1] = 1; // boundary attribute 4 (index 3) is fixed
299 ess_bdr[2] = 1; // boundary attribute 4 (index 3) is fixed
300 ess_bdr[3] = 1; // boundary attribute 4 (index 3) is fixed
301
302 // Same as above, but this is for the thermal operator for HDiv
303 // formulation the essential BC is the flux
304
305 thermal_ess_bdr = 0;
306 thermal_ess_bdr[2] = 1; // boundary attribute 4 (index 3) is fixed
307
308 // Same as above, but this is for the poisson eq for H1 formulation the
309 // essential BC is the value of Phi
310
311 poisson_ess_bdr = 0;
312 poisson_ess_bdr[0] = 1; // boundary attribute 1 (index 0) is fixed
313 poisson_ess_bdr[1] = 1; // boundary attribute 2 (index 1) is fixed
314 // END CODE FOR THE COIL PROBLEM
315 }
316 else if (strcmp(problem,"rod")==0)
317 {
318 // BEGIN CODE FOR THE STRAIGHT ROD PROBLEM
319 // the boundary conditions below are for the straight rod problem
320
321 ess_bdr = 0;
322 ess_bdr[0] = 1; // boundary attribute 1 (index 0) is fixed (front)
323 ess_bdr[1] = 1; // boundary attribute 2 (index 1) is fixed (rear)
324 ess_bdr[2] = 1; // boundary attribute 3 (index 2) is fixed (outer)
325
326 // Same as above, but this is for the thermal operator. For HDiv
327 // formulation the essential BC is the flux, which is zero on the front
328 // and sides. Note the Natural BC is T = 0 on the outer surface.
329
330 thermal_ess_bdr = 0;
331 thermal_ess_bdr[0] = 1; // boundary attribute 1 (index 0) is fixed (front)
332 thermal_ess_bdr[1] = 1; // boundary attribute 2 (index 1) is fixed (rear)
333
334 // Same as above, but this is for the poisson eq for H1 formulation the
335 // essential BC is the value of Phi
336
337 poisson_ess_bdr = 0;
338 poisson_ess_bdr[0] = 1; // boundary attribute 1 (index 0) is fixed (front)
339 poisson_ess_bdr[1] = 1; // boundary attribute 2 (index 1) is fixed (back)
340 // END CODE FOR THE STRAIGHT ROD PROBLEM
341 }
342 else
343 {
344 cerr << "Problem " << problem << " not recognized\n";
345 mfem_error();
346 }
347
348 // The following is required for mesh refinement
349 mesh->EnsureNCMesh();
350
351 // 6. Define the ODE solver used for time integration. Several implicit
352 // methods are available, including singly diagonal implicit Runge-Kutta
353 // (SDIRK).
354 ODESolver *ode_solver;
355 switch (ode_solver_type)
356 {
357 // Implicit L-stable methods
358 case 1: ode_solver = new BackwardEulerSolver; break;
359 case 2: ode_solver = new SDIRK23Solver(2); break;
360 case 3: ode_solver = new SDIRK33Solver; break;
361 // Implicit A-stable methods (not L-stable)
362 case 22: ode_solver = new ImplicitMidpointSolver; break;
363 case 23: ode_solver = new SDIRK23Solver; break;
364 case 34: ode_solver = new SDIRK34Solver; break;
365 default:
366 if (Mpi::Root())
367 {
368 cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
369 }
370 delete mesh;
371 return 3;
372 }
373
374 // 7. Refine the mesh in serial to increase the resolution. In this example
375 // we do 'ser_ref_levels' of uniform refinement, where 'ser_ref_levels' is
376 // a command-line parameter.
377 for (int lev = 0; lev < ser_ref_levels; lev++)
378 {
379 mesh->UniformRefinement();
380 }
381
382 // 8. Define a parallel mesh by a partitioning of the serial mesh. Refine
383 // this mesh further in parallel to increase the resolution. Once the
384 // parallel mesh is defined, the serial mesh can be deleted.
385 ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
386 delete mesh;
387 for (int lev = 0; lev < par_ref_levels; lev++)
388 {
389 pmesh->UniformRefinement();
390 }
391 // Make sure tet-only meshes are marked for local refinement.
392 pmesh->Finalize(true);
393
394 // 9. Apply non-uniform non-conforming mesh refinement to the mesh. The
395 // whole metal region is refined once, before the start of the time loop,
396 // i.e. this is not based on any error estimator.
397 if (amr == 1)
398 {
399 Array<int> ref_list;
400 int numElems = pmesh->GetNE();
401 for (int ielem = 0; ielem < numElems; ielem++)
402 {
403 int thisAtt = pmesh->GetAttribute(ielem);
404 if (thisAtt == 1)
405 {
406 ref_list.Append(ielem);
407 }
408 }
409
410 pmesh->GeneralRefinement(ref_list);
411
412 ref_list.DeleteAll();
413 }
414
415 // 10. Rebalance the mesh. Since the mesh was adaptively refined in a
416 // non-uniform way it will be computationally unbalanced.
417 if (pmesh->Nonconforming())
418 {
419 pmesh->Rebalance();
420 }
421
422 // 11. Define the parallel finite element spaces. We use:
423 //
424 // H(curl) for electric field,
425 // H(div) for magnetic flux,
426 // H(div) for thermal flux,
427 // H(grad)/H1 for electrostatic potential,
428 // L2 for temperature
429
430 // L2 contains discontinuous "cell-center" finite elements, type 2 is
431 // "positive"
432 L2_FECollection L2FEC(order-1, dim);
433
434 // ND contains Nedelec "edge-centered" vector finite elements with continuous
435 // tangential component.
436 ND_FECollection HCurlFEC(order, dim);
437
438 // RT contains Raviart-Thomas "face-centered" vector finite elements with
439 // continuous normal component.
440 RT_FECollection HDivFEC(order-1, dim);
441
442 // H1 contains continuous "node-centered" Lagrange finite elements.
443 H1_FECollection HGradFEC(order, dim);
444
445 ParFiniteElementSpace L2FESpace(pmesh, &L2FEC);
446 ParFiniteElementSpace HCurlFESpace(pmesh, &HCurlFEC);
447 ParFiniteElementSpace HDivFESpace(pmesh, &HDivFEC);
448 ParFiniteElementSpace HGradFESpace(pmesh, &HGradFEC);
449
450 // The terminology is TrueVSize is the unique (non-redundant) number of dofs
451 HYPRE_BigInt glob_size_l2 = L2FESpace.GlobalTrueVSize();
452 HYPRE_BigInt glob_size_nd = HCurlFESpace.GlobalTrueVSize();
453 HYPRE_BigInt glob_size_rt = HDivFESpace.GlobalTrueVSize();
454 HYPRE_BigInt glob_size_h1 = HGradFESpace.GlobalTrueVSize();
455
456 if (Mpi::Root())
457 {
458 cout << "Number of Temperature Flux unknowns: " << glob_size_rt << endl;
459 cout << "Number of Temperature unknowns: " << glob_size_l2 << endl;
460 cout << "Number of Electric Field unknowns: " << glob_size_nd << endl;
461 cout << "Number of Magnetic Field unknowns: " << glob_size_rt << endl;
462 cout << "Number of Electrostatic unknowns: " << glob_size_h1 << endl;
463 }
464
465 int Vsize_l2 = L2FESpace.GetVSize();
466 int Vsize_nd = HCurlFESpace.GetVSize();
467 int Vsize_rt = HDivFESpace.GetVSize();
468 int Vsize_h1 = HGradFESpace.GetVSize();
469
470 // 12. Declare storage for field data.
471 // The big BlockVector stores the fields as
472 // 0 Temperature
473 // 1 Temperature Flux
474 // 2 P field
475 // 3 E field
476 // 4 B field
477 // 5 Joule Heating
478
479 Array<int> true_offset(7);
480 true_offset[0] = 0;
481 true_offset[1] = true_offset[0] + Vsize_l2;
482 true_offset[2] = true_offset[1] + Vsize_rt;
483 true_offset[3] = true_offset[2] + Vsize_h1;
484 true_offset[4] = true_offset[3] + Vsize_nd;
485 true_offset[5] = true_offset[4] + Vsize_rt;
486 true_offset[6] = true_offset[5] + Vsize_l2;
487
488 // The BlockVector is a large contiguous chunk of memory for storing required
489 // data for the hypre vectors, in this case: the temperature L2, the T-flux
490 // HDiv, the E-field HCurl, and the B-field HDiv, and scalar potential P.
491 BlockVector F(true_offset);
492
493 // grid functions E, B, T, F, P, and w which is the Joule heating
494 ParGridFunction E_gf, B_gf, T_gf, F_gf, w_gf, P_gf;
495 T_gf.MakeRef(&L2FESpace,F, true_offset[0]);
496 F_gf.MakeRef(&HDivFESpace,F, true_offset[1]);
497 P_gf.MakeRef(&HGradFESpace,F,true_offset[2]);
498 E_gf.MakeRef(&HCurlFESpace,F,true_offset[3]);
499 B_gf.MakeRef(&HDivFESpace,F, true_offset[4]);
500 w_gf.MakeRef(&L2FESpace,F, true_offset[5]);
501
502 // 13. Get the boundary conditions, set up the exact solution grid functions
503 // These VectorCoefficients have an Eval function. Note that e_exact and
504 // b_exact in this case are exact analytical solutions, taking a 3-vector
505 // point as input and returning a 3-vector field
509 E_exact.SetTime(0.0);
510 B_exact.SetTime(0.0);
511
512 // 14. Initialize the Diffusion operator, the GLVis visualization and print
513 // the initial energies.
514 MagneticDiffusionEOperator oper(true_offset[6], L2FESpace, HCurlFESpace,
515 HDivFESpace, HGradFESpace,
516 ess_bdr, thermal_ess_bdr, poisson_ess_bdr,
517 mu, sigmaMap, TcapMap, InvTcapMap,
518 InvTcondMap);
519
520 // This function initializes all the fields to zero or some provided IC
521 oper.Init(F);
522
523 socketstream vis_T, vis_E, vis_B, vis_w, vis_P;
524 char vishost[] = "localhost";
525 if (visualization)
526 {
527 // Make sure all ranks have sent their 'v' solution before initiating
528 // another set of GLVis connections (one from each rank):
529 MPI_Barrier(pmesh->GetComm());
530
531 vis_T.precision(8);
532 vis_E.precision(8);
533 vis_B.precision(8);
534 vis_P.precision(8);
535 vis_w.precision(8);
536
537 int Wx = 0, Wy = 0; // window position
538 int Ww = 350, Wh = 350; // window size
539 int offx = Ww+10, offy = Wh+45; // window offsets
540
541 VisualizeField(vis_P, vishost, visport,
542 P_gf, "Electric Potential (Phi)", Wx, Wy, Ww, Wh);
543 Wx += offx;
544
545 VisualizeField(vis_E, vishost, visport,
546 E_gf, "Electric Field (E)", Wx, Wy, Ww, Wh);
547 Wx += offx;
548
549 VisualizeField(vis_B, vishost, visport,
550 B_gf, "Magnetic Field (B)", Wx, Wy, Ww, Wh);
551 Wx = 0;
552 Wy += offy;
553
554 VisualizeField(vis_w, vishost, visport,
555 w_gf, "Joule Heating", Wx, Wy, Ww, Wh);
556
557 Wx += offx;
558
559 VisualizeField(vis_T, vishost, visport,
560 T_gf, "Temperature", Wx, Wy, Ww, Wh);
561 }
562 // VisIt visualization
563 VisItDataCollection visit_dc(basename, pmesh);
564 if ( visit )
565 {
566 visit_dc.RegisterField("E", &E_gf);
567 visit_dc.RegisterField("B", &B_gf);
568 visit_dc.RegisterField("T", &T_gf);
569 visit_dc.RegisterField("w", &w_gf);
570 visit_dc.RegisterField("Phi", &P_gf);
571 visit_dc.RegisterField("F", &F_gf);
572
573 visit_dc.SetCycle(0);
574 visit_dc.SetTime(0.0);
575 visit_dc.Save();
576 }
577
578 E_exact.SetTime(0.0);
579 B_exact.SetTime(0.0);
580
581 // 15. Perform time-integration (looping over the time iterations, ti, with a
582 // time-step dt). The object oper is the MagneticDiffusionOperator which
583 // has a Mult() method and an ImplicitSolve() method which are used by
584 // the time integrators.
585 ode_solver->Init(oper);
586 real_t t = 0.0;
587
588 bool last_step = false;
589 for (int ti = 1; !last_step; ti++)
590 {
591 if (t + dt >= t_final - dt/2)
592 {
593 last_step = true;
594 }
595
596 // F is the vector of dofs, t is the current time, and dt is the time step
597 // to advance.
598 ode_solver->Step(F, t, dt);
599
600 if (debug == 1)
601 {
602 oper.Debug(basename,t);
603 }
604
605 if (gfprint == 1)
606 {
607 ostringstream T_name, E_name, B_name, F_name, w_name, P_name, mesh_name;
608 T_name << basename << "_" << setfill('0') << setw(6) << t << "_"
609 << "T." << setfill('0') << setw(6) << myid;
610 E_name << basename << "_" << setfill('0') << setw(6) << t << "_"
611 << "E." << setfill('0') << setw(6) << myid;
612 B_name << basename << "_" << setfill('0') << setw(6) << t << "_"
613 << "B." << setfill('0') << setw(6) << myid;
614 F_name << basename << "_" << setfill('0') << setw(6) << t << "_"
615 << "F." << setfill('0') << setw(6) << myid;
616 w_name << basename << "_" << setfill('0') << setw(6) << t << "_"
617 << "w." << setfill('0') << setw(6) << myid;
618 P_name << basename << "_" << setfill('0') << setw(6) << t << "_"
619 << "P." << setfill('0') << setw(6) << myid;
620 mesh_name << basename << "_" << setfill('0') << setw(6) << t << "_"
621 << "mesh." << setfill('0') << setw(6) << myid;
622
623 ofstream mesh_ofs(mesh_name.str().c_str());
624 mesh_ofs.precision(8);
625 pmesh->Print(mesh_ofs);
626 mesh_ofs.close();
627
628 ofstream T_ofs(T_name.str().c_str());
629 T_ofs.precision(8);
630 T_gf.Save(T_ofs);
631 T_ofs.close();
632
633 ofstream E_ofs(E_name.str().c_str());
634 E_ofs.precision(8);
635 E_gf.Save(E_ofs);
636 E_ofs.close();
637
638 ofstream B_ofs(B_name.str().c_str());
639 B_ofs.precision(8);
640 B_gf.Save(B_ofs);
641 B_ofs.close();
642
643 ofstream F_ofs(F_name.str().c_str());
644 F_ofs.precision(8);
645 F_gf.Save(B_ofs);
646 F_ofs.close();
647
648 ofstream P_ofs(P_name.str().c_str());
649 P_ofs.precision(8);
650 P_gf.Save(P_ofs);
651 P_ofs.close();
652
653 ofstream w_ofs(w_name.str().c_str());
654 w_ofs.precision(8);
655 w_gf.Save(w_ofs);
656 w_ofs.close();
657 }
658
659 if (last_step || (ti % vis_steps) == 0)
660 {
661 real_t el = oper.ElectricLosses(E_gf);
662
663 if (Mpi::Root())
664 {
665 cout << fixed;
666 cout << "step " << setw(6) << ti << ",\tt = " << setw(6)
667 << setprecision(3) << t
668 << ",\tdot(E, J) = " << setprecision(8) << el << endl;
669 }
670
671 // Make sure all ranks have sent their 'v' solution before initiating
672 // another set of GLVis connections (one from each rank):
673 MPI_Barrier(pmesh->GetComm());
674
675 if (visualization)
676 {
677 int Wx = 0, Wy = 0; // window position
678 int Ww = 350, Wh = 350; // window size
679 int offx = Ww+10, offy = Wh+45; // window offsets
680
681 VisualizeField(vis_P, vishost, visport,
682 P_gf, "Electric Potential (Phi)", Wx, Wy, Ww, Wh);
683 Wx += offx;
684
685 VisualizeField(vis_E, vishost, visport,
686 E_gf, "Electric Field (E)", Wx, Wy, Ww, Wh);
687 Wx += offx;
688
689 VisualizeField(vis_B, vishost, visport,
690 B_gf, "Magnetic Field (B)", Wx, Wy, Ww, Wh);
691
692 Wx = 0;
693 Wy += offy;
694
695 VisualizeField(vis_w, vishost, visport,
696 w_gf, "Joule Heating", Wx, Wy, Ww, Wh);
697
698 Wx += offx;
699
700 VisualizeField(vis_T, vishost, visport,
701 T_gf, "Temperature", Wx, Wy, Ww, Wh);
702 }
703
704 if (visit)
705 {
706 visit_dc.SetCycle(ti);
707 visit_dc.SetTime(t);
708 visit_dc.Save();
709 }
710 }
711 }
712 if (visualization)
713 {
714 vis_T.close();
715 vis_E.close();
716 vis_B.close();
717 vis_w.close();
718 vis_P.close();
719 }
720
721 // 16. Free the used memory.
722 delete ode_solver;
723 delete pmesh;
724
725 return 0;
726}
727
728namespace mfem
729{
730
731namespace electromagnetics
732{
733
734void edot_bc(const Vector &x, Vector &E)
735{
736 E = 0.0;
737}
738
739void e_exact(const Vector &x, real_t t, Vector &E)
740{
741 E[0] = 0.0;
742 E[1] = 0.0;
743 E[2] = 0.0;
744}
745
746void b_exact(const Vector &x, real_t t, Vector &B)
747{
748 B[0] = 0.0;
749 B[1] = 0.0;
750 B[2] = 0.0;
751}
752
754{
755 real_t T = 0.0;
756 return T;
757}
758
760{
761 // the value
762 real_t T;
763 if (x[2] < 0.0)
764 {
765 T = 1.0;
766 }
767 else
768 {
769 T = -1.0;
770 }
771
772 return T*cos(wj_ * t);
773}
774
775} // namespace electromagnetics
776
777} // namespace mfem
778
779void display_banner(ostream & os)
780{
781 os << " ____. .__ " << endl
782 << " | | ____ __ __| | ____ " << endl
783 << " | |/ _ \\| | \\ | _/ __ \\ " << endl
784 << "/\\__| ( <_> ) | / |_\\ ___/ " << endl
785 << "\\________|\\____/|____/|____/\\___ >" << endl
786 << " \\/ " << endl
787 << flush;
788}
T Max() const
Find the maximal element in the array, using the comparison operator < for class T.
Definition array.cpp:68
void DeleteAll()
Delete the whole array.
Definition array.hpp:925
int Append(const T &el)
Append element 'el' to array, resize if necessary.
Definition array.hpp:830
Backward Euler ODE solver. L-stable.
Definition ode.hpp:337
A class to handle Vectors in a block fashion.
void SetCycle(int c)
Set time cycle (for time-dependent simulations)
void SetTime(real_t t)
Set physical time (for time-dependent simulations)
int GetVSize() const
Return the number of vector dofs, i.e. GetNDofs() x GetVDim().
Definition fespace.hpp:848
A general function coefficient.
Arbitrary order H1-conforming (continuous) finite elements.
Definition fe_coll.hpp:275
static void Init()
Initialize hypre by calling HYPRE_Init() and set default options. After calling Hypre::Init(),...
Definition hypre.cpp:33
Implicit midpoint method. A-stable, not L-stable.
Definition ode.hpp:350
Arbitrary order "L2-conforming" discontinuous finite elements.
Definition fe_coll.hpp:346
Mesh data type.
Definition mesh.hpp:64
Array< int > bdr_attributes
A list of all unique boundary attributes used by the Mesh.
Definition mesh.hpp:290
void GeneralRefinement(const Array< Refinement > &refinements, int nonconforming=-1, int nc_limit=0)
Definition mesh.cpp:10883
int GetAttribute(int i) const
Return the attribute of element i.
Definition mesh.hpp:1389
bool Nonconforming() const
Return a bool indicating whether this mesh is nonconforming.
Definition mesh.hpp:2367
int GetNE() const
Returns number of elements.
Definition mesh.hpp:1282
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1216
void EnsureNCMesh(bool simplices_nonconforming=false)
Definition mesh.cpp:10951
void UniformRefinement(int i, const DSTable &, int *, int *, int *)
Definition mesh.cpp:11295
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 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.
Definition fe_coll.hpp:482
Abstract class for solving systems of ODEs: dx/dt = f(x,t)
Definition ode.hpp:110
virtual void Init(TimeDependentOperator &f_)
Associate a TimeDependentOperator with the ODE solver.
Definition ode.cpp:161
virtual void Step(Vector &x, real_t &t, real_t &dt)=0
Perform a time step from time t [in] to time t [out] based on the requested step size dt [in].
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.
Abstract parallel finite element space.
Definition pfespace.hpp:29
HYPRE_BigInt GlobalTrueVSize() const
Definition pfespace.hpp:346
Class for parallel grid function.
Definition pgridfunc.hpp:50
void Save(std::ostream &out) const override
void MakeRef(FiniteElementSpace *f, real_t *v) override
Make the ParGridFunction reference external data on a new FiniteElementSpace.
Class for parallel meshes.
Definition pmesh.hpp:34
MPI_Comm GetComm() const
Definition pmesh.hpp:402
void Rebalance()
Definition pmesh.cpp:4008
void Finalize(bool refine=false, bool fix_orientation=false) override
Finalize the construction of a general Mesh.
Definition pmesh.cpp:1523
void Print(std::ostream &out=mfem::out, const std::string &comments="") const override
Definition pmesh.cpp:4800
Arbitrary order H(div)-conforming Raviart-Thomas finite elements.
Definition fe_coll.hpp:403
virtual void SetTime(real_t t)
Set the time for time dependent coefficients.
A general vector function coefficient.
Vector data type.
Definition vector.hpp:82
Data collection with VisIt I/O routines.
void Save() override
Save the collection and a VisIt root file.
void RegisterField(const std::string &field_name, GridFunction *gf) override
Add a grid function to the collection and update the root file.
void Debug(const char *basefilename, real_t time)
real_t ElectricLosses(ParGridFunction &E_gf) const
int close()
Close the socketstream.
real_t sigma(const Vector &x)
Definition maxwell.cpp:102
int problem
Definition ex15.cpp:62
int dim
Definition ex24.cpp:53
real_t freq
Definition ex24.cpp:54
real_t mu
Definition ex25.cpp:140
void E_exact(const Vector &, Vector &)
Definition ex3.cpp:251
int main()
HYPRE_Int HYPRE_BigInt
void display_banner(ostream &os)
Definition joule.cpp:779
int Ww
int Wy
int Wx
int offx
int Wh
int offy
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)
real_t p_bc(const Vector &x, real_t t)
Definition joule.cpp:759
void e_exact(const Vector &x, real_t t, Vector &E)
Definition joule.cpp:739
real_t t_exact(const Vector &x)
Definition joule.cpp:753
void edot_bc(const Vector &x, Vector &E)
Definition joule.cpp:734
void b_exact(const Vector &x, real_t t, Vector &B)
Definition joule.cpp:746
void mfem_error(const char *msg)
Definition error.cpp:154
float real_t
Definition config.hpp:43
const char vishost[]