MFEM v4.8.0
Finite element discretization library
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
ex9p.cpp
Go to the documentation of this file.
1// MFEM Example 9 - Parallel Version
2// SUNDIALS Modification
3//
4// Compile with:
5// make ex9p (GNU make)
6// make sundials_ex9p (CMake)
7//
8// Sample runs:
9// mpirun -np 4 ex9p -m ../../data/periodic-segment.mesh -p 1 -rp 1 -s 7 -dt 0.0025
10// mpirun -np 4 ex9p -m ../../data/periodic-square.mesh -p 1 -rp 1 -s 8 -dt 0.0025 -tf 9
11// mpirun -np 4 ex9p -m ../../data/periodic-hexagon.mesh -p 0 -rp 1 -s 7 -dt 0.0009 -vs 25
12// mpirun -np 4 ex9p -m ../../data/periodic-hexagon.mesh -p 0 -rp 1 -s 9 -dt 0.005 -vs 15
13// mpirun -np 4 ex9p -m ../../data/amr-quad.mesh -p 1 -rp 1 -s 9 -dt 0.001 -tf 9
14// mpirun -np 4 ex9p -m ../../data/star-q3.mesh -p 1 -rp 1 -s 9 -dt 0.0025 -tf 9
15// mpirun -np 4 ex9p -m ../../data/disc-nurbs.mesh -p 1 -rp 2 -s 7 -dt 0.0025 -tf 9
16// mpirun -np 4 ex9p -m ../../data/periodic-cube.mesh -p 0 -rp 1 -s 8 -dt 0.01 -tf 8 -o 2
17//
18// Device sample runs:
19// mpirun -np 4 ex9p -pa
20// mpirun -np 4 ex9p -ea
21// mpirun -np 4 ex9p -fa
22// mpirun -np 4 ex9p -pa -m ../../data/periodic-cube.mesh
23// mpirun -np 4 ex9p -pa -m ../../data/periodic-cube.mesh -d cuda
24// mpirun -np 4 ex9p -ea -m ../../data/periodic-cube.mesh -d cuda
25// mpirun -np 4 ex9p -fa -m ../../data/periodic-cube.mesh -d cuda
26//
27// Description: This example code solves the time-dependent advection equation
28// du/dt + v.grad(u) = 0, where v is a given fluid velocity, and
29// u0(x)=u(0,x) is a given initial condition.
30//
31// The example demonstrates the use of Discontinuous Galerkin (DG)
32// bilinear forms in MFEM (face integrators), the use of implicit
33// and explicit ODE time integrators, the definition of periodic
34// boundary conditions through periodic meshes, as well as the use
35// of GLVis for persistent visualization of a time-evolving
36// solution. Saving of time-dependent data files for visualization
37// with VisIt (visit.llnl.gov) and ParaView (paraview.org), as
38// well as the optional saving with ADIOS2 (adios2.readthedocs.io)
39// are also illustrated.
40
41#include "mfem.hpp"
42#include <fstream>
43#include <iostream>
44
45#ifndef MFEM_USE_SUNDIALS
46#error This example requires that MFEM is built with MFEM_USE_SUNDIALS=YES
47#endif
48
49using namespace std;
50using namespace mfem;
51
52// Choice for the problem setup. The fluid velocity, initial condition and
53// inflow boundary condition are chosen based on this parameter.
55
56// Velocity coefficient
57void velocity_function(const Vector &x, Vector &v);
58
59// Initial condition
60double u0_function(const Vector &x);
61
62// Inflow boundary condition
63double inflow_function(const Vector &x);
64
65// Mesh bounding box
67
68// Type of preconditioner for implicit time integrator
69enum class PrecType : int
70{
71 ILU = 0,
72 AIR = 1
73};
74
75#if MFEM_HYPRE_VERSION >= 21800
76// Algebraic multigrid preconditioner for advective problems based on
77// approximate ideal restriction (AIR). Most effective when matrix is
78// first scaled by DG block inverse, and AIR applied to scaled matrix.
79// See https://doi.org/10.1137/17M1144350.
80class AIR_prec : public Solver
81{
82private:
83 const HypreParMatrix *A;
84 // Copy of A scaled by block-diagonal inverse
86
87 HypreBoomerAMG *AIR_solver;
88 int blocksize;
89
90public:
91 AIR_prec(int blocksize_) : AIR_solver(NULL), blocksize(blocksize_) { }
92
93 void SetOperator(const Operator &op)
94 {
95 width = op.Width();
96 height = op.Height();
97
98 A = dynamic_cast<const HypreParMatrix *>(&op);
99 MFEM_VERIFY(A != NULL, "AIR_prec requires a HypreParMatrix.")
100
101 // Scale A by block-diagonal inverse
102 BlockInverseScale(A, &A_s, NULL, NULL, blocksize,
104 delete AIR_solver;
105 AIR_solver = new HypreBoomerAMG(A_s);
106 AIR_solver->SetAdvectiveOptions(1, "", "FA");
107 AIR_solver->SetPrintLevel(0);
108 AIR_solver->SetMaxLevels(50);
109 }
110
111 virtual void Mult(const Vector &x, Vector &y) const
112 {
113 // Scale the rhs by block inverse and solve system
114 HypreParVector z_s;
115 BlockInverseScale(A, NULL, &x, &z_s, blocksize,
116 BlockInverseScaleJob::RHS_ONLY);
117 AIR_solver->Mult(z_s, y);
118 }
119
120 ~AIR_prec()
121 {
122 delete AIR_solver;
123 }
124};
125#endif
126
127
128class DG_Solver : public Solver
129{
130private:
131 HypreParMatrix &M, &K;
132 SparseMatrix M_diag;
134 GMRESSolver linear_solver;
135 Solver *prec;
136 double dt;
137public:
138 DG_Solver(HypreParMatrix &M_, HypreParMatrix &K_, const FiniteElementSpace &fes,
139 PrecType prec_type)
140 : M(M_),
141 K(K_),
142 A(NULL),
143 linear_solver(M.GetComm()),
144 dt(-1.0)
145 {
146 int block_size = fes.GetTypicalFE()->GetDof();
147 if (prec_type == PrecType::ILU)
148 {
149 prec = new BlockILU(block_size,
150 BlockILU::Reordering::MINIMUM_DISCARDED_FILL);
151 }
152 else if (prec_type == PrecType::AIR)
153 {
154#if MFEM_HYPRE_VERSION >= 21800
155 prec = new AIR_prec(block_size);
156#else
157 MFEM_ABORT("Must have MFEM_HYPRE_VERSION >= 21800 to use AIR.\n");
158#endif
159 }
160 linear_solver.iterative_mode = false;
161 linear_solver.SetRelTol(1e-9);
162 linear_solver.SetAbsTol(0.0);
163 linear_solver.SetMaxIter(100);
164 linear_solver.SetPrintLevel(0);
165 linear_solver.SetPreconditioner(*prec);
166
167 M.GetDiag(M_diag);
168 }
169
170 void SetTimeStep(double dt_)
171 {
172 if (dt_ != dt)
173 {
174 dt = dt_;
175 // Form operator A = M - dt*K
176 delete A;
177 A = Add(-dt, K, 0.0, K);
178 SparseMatrix A_diag;
179 A->GetDiag(A_diag);
180 A_diag.Add(1.0, M_diag);
181 // this will also call SetOperator on the preconditioner
182 linear_solver.SetOperator(*A);
183 }
184 }
185
186 void SetOperator(const Operator &op)
187 {
188 linear_solver.SetOperator(op);
189 }
190
191 virtual void Mult(const Vector &x, Vector &y) const
192 {
193 linear_solver.Mult(x, y);
194 }
195
196 ~DG_Solver()
197 {
198 delete prec;
199 delete A;
200 }
201};
202
203
204/** A time-dependent operator for the right-hand side of the ODE. The DG weak
205 form of du/dt = -v.grad(u) is M du/dt = K u + b, where M and K are the mass
206 and advection matrices, and b describes the flow on the boundary. This can
207 be written as a general ODE, du/dt = M^{-1} (K u + b), and this class is
208 used to evaluate the right-hand side. */
209class FE_Evolution : public TimeDependentOperator
210{
211private:
212 OperatorHandle M, K;
213 const Vector &b;
214 Solver *M_prec;
215 CGSolver M_solver;
216 DG_Solver *dg_solver;
217
218 mutable Vector z;
219
220public:
221 FE_Evolution(ParBilinearForm &M_, ParBilinearForm &K_, const Vector &b_,
222 PrecType prec_type);
223
224 virtual void Mult(const Vector &x, Vector &y) const;
225 virtual void ImplicitSolve(const double dt, const Vector &x, Vector &k);
226
227 virtual ~FE_Evolution();
228};
229
230
231int main(int argc, char *argv[])
232{
233 // 1. Initialize MPI, HYPRE, and SUNDIALS.
234 Mpi::Init(argc, argv);
235 int num_procs = Mpi::WorldSize();
236 int myid = Mpi::WorldRank();
237 Hypre::Init();
239
240 // 2. Parse command-line options.
241 problem = 0;
242 const char *mesh_file = "../../data/periodic-hexagon.mesh";
243 int ser_ref_levels = 2;
244 int par_ref_levels = 0;
245 int order = 3;
246 bool pa = false;
247 bool ea = false;
248 bool fa = false;
249 const char *device_config = "cpu";
250 int ode_solver_type = 7;
251 double t_final = 10.0;
252 double dt = 0.01;
253 bool visualization = false;
254 bool visit = false;
255 bool paraview = false;
256 bool adios2 = false;
257 bool binary = false;
258 int vis_steps = 5;
259#if MFEM_HYPRE_VERSION >= 21800
260 PrecType prec_type = PrecType::AIR;
261#else
262 PrecType prec_type = PrecType::ILU;
263#endif
264
265 // Relative and absolute tolerances for CVODE and ARKODE.
266 const double reltol = 1e-2, abstol = 1e-2;
267
268 int precision = 8;
269 cout.precision(precision);
270
271 OptionsParser args(argc, argv);
272 args.AddOption(&mesh_file, "-m", "--mesh",
273 "Mesh file to use.");
274 args.AddOption(&problem, "-p", "--problem",
275 "Problem setup to use. See options in velocity_function().");
276 args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
277 "Number of times to refine the mesh uniformly in serial.");
278 args.AddOption(&par_ref_levels, "-rp", "--refine-parallel",
279 "Number of times to refine the mesh uniformly in parallel.");
280 args.AddOption(&order, "-o", "--order",
281 "Order (degree) of the finite elements.");
282 args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
283 "--no-partial-assembly", "Enable Partial Assembly.");
284 args.AddOption(&ea, "-ea", "--element-assembly", "-no-ea",
285 "--no-element-assembly", "Enable Element Assembly.");
286 args.AddOption(&fa, "-fa", "--full-assembly", "-no-fa",
287 "--no-full-assembly", "Enable Full Assembly.");
288 args.AddOption(&device_config, "-d", "--device",
289 "Device configuration string, see Device::Configure().");
290 args.AddOption(&ode_solver_type, "-s", "--ode-solver",
291 "ODE solver:\n\t"
292 "1 - Forward Euler,\n\t"
293 "2 - RK2 SSP,\n\t"
294 "3 - RK3 SSP,\n\t"
295 "4 - RK4,\n\t"
296 "6 - RK6,\n\t"
297 "7 - CVODE (adaptive order implicit Adams),\n\t"
298 "8 - ARKODE default (4th order) explicit,\n\t"
299 "9 - ARKODE RK8.");
300 args.AddOption(&t_final, "-tf", "--t-final",
301 "Final time; start time is 0.");
302 args.AddOption(&dt, "-dt", "--time-step",
303 "Time step.");
304 args.AddOption((int *)&prec_type, "-pt", "--prec-type", "Preconditioner for "
305 "implicit solves. 0 for ILU, 1 for pAIR-AMG.");
306 args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
307 "--no-visualization",
308 "Enable or disable GLVis visualization.");
309 args.AddOption(&visit, "-visit", "--visit-datafiles", "-no-visit",
310 "--no-visit-datafiles",
311 "Save data files for VisIt (visit.llnl.gov) visualization.");
312 args.AddOption(&paraview, "-paraview", "--paraview-datafiles", "-no-paraview",
313 "--no-paraview-datafiles",
314 "Save data files for ParaView (paraview.org) visualization.");
315 args.AddOption(&adios2, "-adios2", "--adios2-streams", "-no-adios2",
316 "--no-adios2-streams",
317 "Save data using adios2 streams.");
318 args.AddOption(&binary, "-binary", "--binary-datafiles", "-ascii",
319 "--ascii-datafiles",
320 "Use binary (Sidre) or ascii format for VisIt data files.");
321 args.AddOption(&vis_steps, "-vs", "--visualization-steps",
322 "Visualize every n-th timestep.");
323 args.Parse();
324 if (!args.Good())
325 {
326 if (Mpi::Root())
327 {
328 args.PrintUsage(cout);
329 }
330 return 1;
331 }
332 if (Mpi::Root())
333 {
334 args.PrintOptions(cout);
335 }
336
337 // check for valid ODE solver option
338 if (ode_solver_type < 1 || ode_solver_type > 9)
339 {
340 if (Mpi::Root())
341 {
342 cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
343 }
344 return 3;
345 }
346
347 Device device(device_config);
348 if (Mpi::Root()) { device.Print(); }
349
350 // 3. Read the serial mesh from the given mesh file on all processors. We can
351 // handle geometrically periodic meshes in this code.
352 Mesh *mesh = new Mesh(mesh_file, 1, 1);
353 int dim = mesh->Dimension();
354
355 // 4. Refine the mesh in serial to increase the resolution. In this example
356 // we do 'ser_ref_levels' of uniform refinement, where 'ser_ref_levels' is
357 // a command-line parameter. If the mesh is of NURBS type, we convert it
358 // to a (piecewise-polynomial) high-order mesh.
359 for (int lev = 0; lev < ser_ref_levels; lev++)
360 {
361 mesh->UniformRefinement();
362 }
363 if (mesh->NURBSext)
364 {
365 mesh->SetCurvature(max(order, 1));
366 }
367 mesh->GetBoundingBox(bb_min, bb_max, max(order, 1));
368
369 // 5. Define the parallel mesh by a partitioning of the serial mesh. Refine
370 // this mesh further in parallel to increase the resolution. Once the
371 // parallel mesh is defined, the serial mesh can be deleted.
372 ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
373 delete mesh;
374 for (int lev = 0; lev < par_ref_levels; lev++)
375 {
376 pmesh->UniformRefinement();
377 }
378
379 // 6. Define the parallel discontinuous DG finite element space on the
380 // parallel refined mesh of the given polynomial order.
382 ParFiniteElementSpace *fes = new ParFiniteElementSpace(pmesh, &fec);
383
384 HYPRE_BigInt global_vSize = fes->GlobalTrueVSize();
385 if (Mpi::Root())
386 {
387 cout << "Number of unknowns: " << global_vSize << endl;
388 }
389
390 // 7. Set up and assemble the parallel bilinear and linear forms (and the
391 // parallel hypre matrices) corresponding to the DG discretization. The
392 // DGTraceIntegrator involves integrals over mesh interior faces.
396
397 ParBilinearForm *m = new ParBilinearForm(fes);
398 ParBilinearForm *k = new ParBilinearForm(fes);
399 if (pa)
400 {
401 m->SetAssemblyLevel(AssemblyLevel::PARTIAL);
402 k->SetAssemblyLevel(AssemblyLevel::PARTIAL);
403 }
404 else if (ea)
405 {
406 m->SetAssemblyLevel(AssemblyLevel::ELEMENT);
407 k->SetAssemblyLevel(AssemblyLevel::ELEMENT);
408 }
409 else if (fa)
410 {
411 m->SetAssemblyLevel(AssemblyLevel::FULL);
412 k->SetAssemblyLevel(AssemblyLevel::FULL);
413 }
414
416 constexpr double alpha = -1.0;
422
423 ParLinearForm *b = new ParLinearForm(fes);
424 b->AddBdrFaceIntegrator(
425 new BoundaryFlowIntegrator(inflow, velocity, alpha));
426
427 int skip_zeros = 0;
428 m->Assemble();
429 k->Assemble(skip_zeros);
430 b->Assemble();
431 m->Finalize();
432 k->Finalize(skip_zeros);
433
434 HypreParVector *B = b->ParallelAssemble();
435
436 // 8. Define the initial conditions, save the corresponding grid function to
437 // a file and (optionally) save data in the VisIt format and initialize
438 // GLVis visualization.
440 u->ProjectCoefficient(u0);
441 HypreParVector *U = u->GetTrueDofs();
442
443 {
444 ostringstream mesh_name, sol_name;
445 mesh_name << "ex9-mesh." << setfill('0') << setw(6) << myid;
446 sol_name << "ex9-init." << setfill('0') << setw(6) << myid;
447 ofstream omesh(mesh_name.str().c_str());
448 omesh.precision(precision);
449 pmesh->Print(omesh);
450 ofstream osol(sol_name.str().c_str());
451 osol.precision(precision);
452 u->Save(osol);
453 }
454
455 // Create data collection for solution output: either VisItDataCollection for
456 // ascii data files, or SidreDataCollection for binary data files.
457 DataCollection *dc = NULL;
458 if (visit)
459 {
460 if (binary)
461 {
462#ifdef MFEM_USE_SIDRE
463 dc = new SidreDataCollection("Example9-Parallel", pmesh);
464#else
465 MFEM_ABORT("Must build with MFEM_USE_SIDRE=YES for binary output.");
466#endif
467 }
468 else
469 {
470 dc = new VisItDataCollection("Example9-Parallel", pmesh);
471 dc->SetPrecision(precision);
472 // To save the mesh using MFEM's parallel mesh format:
473 // dc->SetFormat(DataCollection::PARALLEL_FORMAT);
474 }
475 dc->RegisterField("solution", u);
476 dc->SetCycle(0);
477 dc->SetTime(0.0);
478 dc->Save();
479 }
480
481 ParaViewDataCollection *pd = NULL;
482 if (paraview)
483 {
484 pd = new ParaViewDataCollection("Example9P", pmesh);
485 pd->SetPrefixPath("ParaView");
486 pd->RegisterField("solution", u);
487 pd->SetLevelsOfDetail(order);
488 pd->SetDataFormat(VTKFormat::BINARY);
489 pd->SetHighOrderOutput(true);
490 pd->SetCycle(0);
491 pd->SetTime(0.0);
492 pd->Save();
493 }
494
495 // Optionally output a BP (binary pack) file using ADIOS2. This can be
496 // visualized with the ParaView VTX reader.
497#ifdef MFEM_USE_ADIOS2
498 ADIOS2DataCollection *adios2_dc = NULL;
499 if (adios2)
500 {
501 std::string postfix(mesh_file);
502 postfix.erase(0, std::string("../data/").size() );
503 postfix += "_o" + std::to_string(order);
504 const std::string collection_name = "ex9-p-" + postfix + ".bp";
505
506 adios2_dc = new ADIOS2DataCollection(MPI_COMM_WORLD, collection_name, pmesh);
507 // output data substreams are half the number of mpi processes
508 adios2_dc->SetParameter("SubStreams", std::to_string(num_procs/2) );
509 adios2_dc->RegisterField("solution", u);
510 adios2_dc->SetCycle(0);
511 adios2_dc->SetTime(0.0);
512 adios2_dc->Save();
513 }
514#endif
515
516 socketstream sout;
517 if (visualization)
518 {
519 char vishost[] = "localhost";
520 int visport = 19916;
521 sout.open(vishost, visport);
522 if (!sout)
523 {
524 if (Mpi::Root())
525 {
526 cout << "Unable to connect to GLVis server at "
527 << vishost << ':' << visport << endl;
528 }
529 visualization = false;
530 if (Mpi::Root())
531 {
532 cout << "GLVis visualization disabled.\n";
533 }
534 }
535 else
536 {
537 sout << "parallel " << num_procs << " " << myid << "\n";
538 sout.precision(precision);
539 sout << "solution\n" << *pmesh << *u;
540 sout << "pause\n";
541 sout << flush;
542 if (Mpi::Root())
543 {
544 cout << "GLVis visualization paused."
545 << " Press space (in the GLVis window) to resume it.\n";
546 }
547 }
548 }
549
550 // 9. Define the time-dependent evolution operator describing the ODE
551 // right-hand side, and define the ODE solver used for time integration.
552 FE_Evolution adv(*m, *k, *B, prec_type);
553
554 double t = 0.0;
555 adv.SetTime(t);
556
557 // Create the time integrator
558 ODESolver *ode_solver = NULL;
559 CVODESolver *cvode = NULL;
560 ARKStepSolver *arkode = NULL;
561 switch (ode_solver_type)
562 {
563 case 1: ode_solver = new ForwardEulerSolver; break;
564 case 2: ode_solver = new RK2Solver(1.0); break;
565 case 3: ode_solver = new RK3SSPSolver; break;
566 case 4: ode_solver = new RK4Solver; break;
567 case 6: ode_solver = new RK6Solver; break;
568 case 7:
569 cvode = new CVODESolver(MPI_COMM_WORLD, CV_ADAMS);
570 cvode->Init(adv);
571 cvode->SetSStolerances(reltol, abstol);
572 cvode->SetMaxStep(dt);
574 ode_solver = cvode; break;
575 case 8:
576 case 9:
577 arkode = new ARKStepSolver(MPI_COMM_WORLD, ARKStepSolver::EXPLICIT);
578 arkode->Init(adv);
579 arkode->SetSStolerances(reltol, abstol);
580 arkode->SetMaxStep(dt);
581 if (ode_solver_type == 9)
582 {
584 }
585 ode_solver = arkode; break;
586 }
587
588 // Initialize MFEM integrators, SUNDIALS integrators are initialized above
589 if (ode_solver_type < 7) { ode_solver->Init(adv); }
590
591 // 10. Perform time-integration (looping over the time iterations, ti,
592 // with a time-step dt).
593 bool done = false;
594 for (int ti = 0; !done; )
595 {
596 double dt_real = min(dt, t_final - t);
597 ode_solver->Step(*U, t, dt_real);
598 ti++;
599
600 done = (t >= t_final - 1e-8*dt);
601
602 if (done || ti % vis_steps == 0)
603 {
604 if (Mpi::Root())
605 {
606 cout << "time step: " << ti << ", time: " << t << endl;
607 if (cvode) { cvode->PrintInfo(); }
608 if (arkode) { arkode->PrintInfo(); }
609 }
610
611 // 11. Extract the parallel grid function corresponding to the finite
612 // element approximation U (the local solution on each processor).
613 *u = *U;
614
615 if (visualization)
616 {
617 sout << "parallel " << num_procs << " " << myid << "\n";
618 sout << "solution\n" << *pmesh << *u << flush;
619 }
620
621 if (visit)
622 {
623 dc->SetCycle(ti);
624 dc->SetTime(t);
625 dc->Save();
626 }
627
628 if (paraview)
629 {
630 pd->SetCycle(ti);
631 pd->SetTime(t);
632 pd->Save();
633 }
634
635#ifdef MFEM_USE_ADIOS2
636 // transient solutions can be visualized with ParaView
637 if (adios2)
638 {
639 adios2_dc->SetCycle(ti);
640 adios2_dc->SetTime(t);
641 adios2_dc->Save();
642 }
643#endif
644 }
645 }
646
647 // 12. Save the final solution in parallel. This output can be viewed later
648 // using GLVis: "glvis -np <np> -m ex9-mesh -g ex9-final".
649 {
650 *u = *U;
651 ostringstream sol_name;
652 sol_name << "ex9-final." << setfill('0') << setw(6) << myid;
653 ofstream osol(sol_name.str().c_str());
654 osol.precision(precision);
655 u->Save(osol);
656 }
657
658 // 13. Free the used memory.
659 delete U;
660 delete u;
661 delete B;
662 delete b;
663 delete k;
664 delete m;
665 delete fes;
666 delete pmesh;
667 delete ode_solver;
668 delete pd;
669#ifdef MFEM_USE_ADIOS2
670 if (adios2)
671 {
672 delete adios2_dc;
673 }
674#endif
675 delete dc;
676
677 return 0;
678}
679
680
681// Implementation of class FE_Evolution
682FE_Evolution::FE_Evolution(ParBilinearForm &M_, ParBilinearForm &K_,
683 const Vector &b_, PrecType prec_type)
684 : TimeDependentOperator(M_.ParFESpace()->GetTrueVSize()),
685 b(b_),
686 M_solver(M_.ParFESpace()->GetComm()),
687 z(height)
688{
689 if (M_.GetAssemblyLevel()==AssemblyLevel::LEGACY)
690 {
691 M.Reset(M_.ParallelAssemble(), true);
692 K.Reset(K_.ParallelAssemble(), true);
693 }
694 else
695 {
696 M.Reset(&M_, false);
697 K.Reset(&K_, false);
698 }
699
700 M_solver.SetOperator(*M);
701
702 Array<int> ess_tdof_list;
703 if (M_.GetAssemblyLevel()==AssemblyLevel::LEGACY)
704 {
705 HypreParMatrix &M_mat = *M.As<HypreParMatrix>();
706 HypreParMatrix &K_mat = *K.As<HypreParMatrix>();
707 HypreSmoother *hypre_prec = new HypreSmoother(M_mat, HypreSmoother::Jacobi);
708 M_prec = hypre_prec;
709
710 dg_solver = new DG_Solver(M_mat, K_mat, *M_.FESpace(), prec_type);
711 }
712 else
713 {
714 M_prec = new OperatorJacobiSmoother(M_, ess_tdof_list);
715 dg_solver = NULL;
716 }
717
718 M_solver.SetPreconditioner(*M_prec);
719 M_solver.iterative_mode = false;
720 M_solver.SetRelTol(1e-9);
721 M_solver.SetAbsTol(0.0);
722 M_solver.SetMaxIter(100);
723 M_solver.SetPrintLevel(0);
724}
725
726// Solve the equation:
727// u_t = M^{-1}(Ku + b),
728// by solving associated linear system
729// (M - dt*K) d = K*u + b
730void FE_Evolution::ImplicitSolve(const double dt, const Vector &x, Vector &k)
731{
732 K->Mult(x, z);
733 z += b;
734 dg_solver->SetTimeStep(dt);
735 dg_solver->Mult(z, k);
736}
737
738void FE_Evolution::Mult(const Vector &x, Vector &y) const
739{
740 // y = M^{-1} (K x + b)
741 K->Mult(x, z);
742 z += b;
743 M_solver.Mult(z, y);
744}
745
746FE_Evolution::~FE_Evolution()
747{
748 delete M_prec;
749 delete dg_solver;
750}
751
752
753// Velocity coefficient
755{
756 int dim = x.Size();
757
758 // map to the reference [-1,1] domain
759 Vector X(dim);
760 for (int i = 0; i < dim; i++)
761 {
762 double center = (bb_min[i] + bb_max[i]) * 0.5;
763 X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]);
764 }
765
766 switch (problem)
767 {
768 case 0:
769 {
770 // Translations in 1D, 2D, and 3D
771 switch (dim)
772 {
773 case 1: v(0) = 1.0; break;
774 case 2: v(0) = sqrt(2./3.); v(1) = sqrt(1./3.); break;
775 case 3: v(0) = sqrt(3./6.); v(1) = sqrt(2./6.); v(2) = sqrt(1./6.);
776 break;
777 }
778 break;
779 }
780 case 1:
781 case 2:
782 {
783 // Clockwise rotation in 2D around the origin
784 const double w = M_PI/2;
785 switch (dim)
786 {
787 case 1: v(0) = 1.0; break;
788 case 2: v(0) = w*X(1); v(1) = -w*X(0); break;
789 case 3: v(0) = w*X(1); v(1) = -w*X(0); v(2) = 0.0; break;
790 }
791 break;
792 }
793 case 3:
794 {
795 // Clockwise twisting rotation in 2D around the origin
796 const double w = M_PI/2;
797 double d = max((X(0)+1.)*(1.-X(0)),0.) * max((X(1)+1.)*(1.-X(1)),0.);
798 d = d*d;
799 switch (dim)
800 {
801 case 1: v(0) = 1.0; break;
802 case 2: v(0) = d*w*X(1); v(1) = -d*w*X(0); break;
803 case 3: v(0) = d*w*X(1); v(1) = -d*w*X(0); v(2) = 0.0; break;
804 }
805 break;
806 }
807 }
808}
809
810// Initial condition
811double u0_function(const Vector &x)
812{
813 int dim = x.Size();
814
815 // map to the reference [-1,1] domain
816 Vector X(dim);
817 for (int i = 0; i < dim; i++)
818 {
819 double center = (bb_min[i] + bb_max[i]) * 0.5;
820 X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]);
821 }
822
823 switch (problem)
824 {
825 case 0:
826 case 1:
827 {
828 switch (dim)
829 {
830 case 1:
831 return exp(-40.*pow(X(0)-0.5,2));
832 case 2:
833 case 3:
834 {
835 double rx = 0.45, ry = 0.25, cx = 0., cy = -0.2, w = 10.;
836 if (dim == 3)
837 {
838 const double s = (1. + 0.25*cos(2*M_PI*X(2)));
839 rx *= s;
840 ry *= s;
841 }
842 return ( erfc(w*(X(0)-cx-rx))*erfc(-w*(X(0)-cx+rx)) *
843 erfc(w*(X(1)-cy-ry))*erfc(-w*(X(1)-cy+ry)) )/16;
844 }
845 }
846 }
847 case 2:
848 {
849 double x_ = X(0), y_ = X(1), rho, phi;
850 rho = hypot(x_, y_);
851 phi = atan2(y_, x_);
852 return pow(sin(M_PI*rho),2)*sin(3*phi);
853 }
854 case 3:
855 {
856 const double f = M_PI;
857 return sin(f*X(0))*sin(f*X(1));
858 }
859 }
860 return 0.0;
861}
862
863// Inflow boundary condition (zero for the problems considered in this example)
864double inflow_function(const Vector &x)
865{
866 switch (problem)
867 {
868 case 0:
869 case 1:
870 case 2:
871 case 3: return 0.0;
872 }
873 return 0.0;
874}
void SetParameter(const std::string key, const std::string value) noexcept
Interface to ARKode's ARKStep module – additive Runge-Kutta methods.
Definition sundials.hpp:711
void SetMaxStep(double dt_max)
Set the maximum time step.
void PrintInfo() const
Print various ARKStep statistics.
@ EXPLICIT
Explicit RK method.
Definition sundials.hpp:716
void Init(TimeDependentOperator &f_) override
Initialize ARKode: calls ARKStepCreate() to create the ARKStep memory and set some defaults.
void SetERKTableNum(ARKODE_ERKTableID table_id)
Choose a specific Butcher table for an explicit RK method.
void SetSStolerances(double reltol, double abstol)
Set the scalar relative and scalar absolute tolerances.
@ GaussLobatto
Closed type.
Definition fe_base.hpp:36
void SetAssemblyLevel(AssemblyLevel assembly_level)
Set the desired assembly level.
void AddDomainIntegrator(BilinearFormIntegrator *bfi)
Adds new Domain Integrator. Assumes ownership of bfi.
void Finalize(int skip_zeros=1) override
Finalizes the matrix initialization if the AssemblyLevel is AssemblyLevel::LEGACY....
FiniteElementSpace * FESpace()
Return the FE space associated with the BilinearForm.
AssemblyLevel GetAssemblyLevel() const
Returns the assembly level.
void AddBdrFaceIntegrator(BilinearFormIntegrator *bfi)
Adds new boundary Face Integrator. Assumes ownership of bfi.
void AddInteriorFaceIntegrator(BilinearFormIntegrator *bfi)
Adds new interior Face Integrator. Assumes ownership of bfi.
Conjugate gradient method.
Definition solvers.hpp:538
void Mult(const Vector &b, Vector &x) const override
Iterative solution of the linear system using the Conjugate Gradient method.
Definition solvers.cpp:751
Interface to the CVODE library – linear multi-step methods.
Definition sundials.hpp:420
void SetSStolerances(double reltol, double abstol)
Set the scalar relative and scalar absolute tolerances.
Definition sundials.cpp:901
void Init(TimeDependentOperator &f_) override
Initialize CVODE: calls CVodeCreate() to create the CVODE memory and set some defaults.
Definition sundials.cpp:735
void SetMaxStep(double dt_max)
Set the maximum time step.
Definition sundials.cpp:919
void PrintInfo() const
Print various CVODE statistics.
Definition sundials.cpp:945
void UseSundialsLinearSolver()
Attach SUNDIALS GMRES linear solver to CVODE.
Definition sundials.cpp:881
void SetPrecision(int prec)
Set the precision (number of digits) used for the text output of doubles.
virtual void RegisterField(const std::string &field_name, GridFunction *gf)
Add a grid function to the collection.
void SetCycle(int c)
Set time cycle (for time-dependent simulations)
void SetTime(real_t t)
Set physical time (for time-dependent simulations)
void SetPrefixPath(const std::string &prefix)
Set the path where the DataCollection will be saved.
virtual void Save()
Save the collection to disk.
The MFEM Device class abstracts hardware devices such as GPUs, as well as programming models such as ...
Definition device.hpp:123
void Print(std::ostream &out=mfem::out)
Print the configuration of the MFEM virtual device object.
Definition device.cpp:297
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition fespace.hpp:244
const FiniteElement * GetTypicalFE() const
Return GetFE(0) if the local mesh is not empty; otherwise return a typical FE based on the Geometry t...
Definition fespace.cpp:3871
int GetDof() const
Returns the number of degrees of freedom in the finite element.
Definition fe_base.hpp:334
The classical forward Euler method.
Definition ode.hpp:226
A general function coefficient.
GMRES method.
Definition solvers.hpp:572
void Mult(const Vector &b, Vector &x) const override
Iterative solution of the linear system using the GMRES method.
Definition solvers.cpp:1016
The BoomerAMG solver in hypre.
Definition hypre.hpp:1717
Wrapper for hypre's ParCSR matrix class.
Definition hypre.hpp:408
void GetDiag(Vector &diag) const
Get the local diagonal of the matrix.
Definition hypre.cpp:1605
HYPRE_Int Mult(HypreParVector &x, HypreParVector &y, real_t alpha=1.0, real_t beta=0.0) const
Computes y = alpha * A * x + beta * y.
Definition hypre.cpp:1861
Wrapper for hypre's parallel vector class.
Definition hypre.hpp:219
Parallel smoothers in hypre.
Definition hypre.hpp:1046
static void Init()
Initialize hypre by calling HYPRE_Init() and set default options. After calling Hypre::Init(),...
Definition hypre.cpp:33
void SetOperator(const Operator &op) override
Also calls SetOperator for the preconditioner if there is one.
Definition solvers.cpp:180
void SetRelTol(real_t rtol)
Definition solvers.hpp:229
virtual void SetPreconditioner(Solver &pr)
This should be called before SetOperator.
Definition solvers.cpp:174
virtual void SetPrintLevel(int print_lvl)
Legacy method to set the level of verbosity of the solver output.
Definition solvers.cpp:72
void SetMaxIter(int max_it)
Definition solvers.hpp:231
void SetAbsTol(real_t atol)
Definition solvers.hpp:230
Arbitrary order "L2-conforming" discontinuous finite elements.
Definition fe_coll.hpp:346
Mesh data type.
Definition mesh.hpp:64
NURBSExtension * NURBSext
Optional NURBS mesh extension.
Definition mesh.hpp:298
void GetBoundingBox(Vector &min, Vector &max, int ref=2)
Returns the minimum and maximum corners of the mesh bounding box.
Definition mesh.cpp:138
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1216
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:6484
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 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).
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].
Pointer to an Operator of a specified type.
Definition handle.hpp:34
Jacobi smoothing for a given bilinear form (no matrix necessary).
Definition solvers.hpp:333
Abstract operator.
Definition operator.hpp:25
int width
Dimension of the input / number of columns in the matrix.
Definition operator.hpp:28
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
Definition operator.hpp:66
int height
Dimension of the output / number of rows in the matrix.
Definition operator.hpp:27
int Width() const
Get the width (size of input) of the Operator. Synonym with NumCols().
Definition operator.hpp:72
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 bilinear form.
HypreParMatrix * ParallelAssemble()
Returns the matrix assembled on the true dofs, i.e. P^t A P.
void Assemble(int skip_zeros=1)
Assemble the local matrix.
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
Class for parallel linear form.
Class for parallel meshes.
Definition pmesh.hpp:34
void Print(std::ostream &out=mfem::out, const std::string &comments="") const override
Definition pmesh.cpp:4800
Helper class for ParaView visualization data.
void SetHighOrderOutput(bool high_order_output_)
void SetLevelsOfDetail(int levels_of_detail_)
void SetDataFormat(VTKFormat fmt)
Third-order, strong stability preserving (SSP) Runge-Kutta method.
Definition ode.hpp:259
The classical explicit forth-order Runge-Kutta method, RK4.
Definition ode.hpp:272
Data collection with Sidre routines following the Conduit mesh blueprint specification.
Base class for solvers.
Definition operator.hpp:780
bool iterative_mode
If true, use the second argument of Mult() as an initial guess.
Definition operator.hpp:783
Data type sparse matrix.
Definition sparsemat.hpp:51
void Add(const int i, const int j, const real_t val)
static void Init()
Definition sundials.cpp:174
Base abstract class for first order time dependent operators.
Definition operator.hpp:332
virtual void SetTime(const real_t t_)
Set the current time.
Definition operator.hpp:394
A general vector function coefficient.
Vector data type.
Definition vector.hpp:82
int Size() const
Returns the size of the vector.
Definition vector.hpp:226
Data collection with VisIt I/O routines.
int open(const char hostname[], int port)
Open the socket stream on 'port' at 'hostname'.
const real_t alpha
Definition ex15.cpp:369
int dim
Definition ex24.cpp:53
PrecType
Definition ex9p.cpp:72
int main()
HYPRE_Int HYPRE_BigInt
real_t b
Definition lissajous.cpp:42
real_t u(const Vector &xvec)
Definition lor_mms.hpp:22
void Mult(const Table &A, const Table &B, Table &C)
C = A * B (as boolean matrices)
Definition table.cpp:548
BlockInverseScaleJob
Definition hypre.hpp:986
void BlockInverseScale(const HypreParMatrix *A, HypreParMatrix *C, const Vector *b, HypreParVector *d, int blocksize, BlockInverseScaleJob job)
Definition hypre.cpp:2920
std::function< real_t(const Vector &)> f(real_t mass_coeff)
Definition lor_mms.hpp:30
void Add(const DenseMatrix &A, const DenseMatrix &B, real_t alpha, DenseMatrix &C)
C = A + alpha*B.
const char vishost[]
void velocity_function(const Vector &x, Vector &v)
Definition ex9p.cpp:754
double u0_function(const Vector &x)
Definition ex9p.cpp:811
int problem
Definition ex9p.cpp:54
PrecType
Definition ex9p.cpp:70
double inflow_function(const Vector &x)
Definition ex9p.cpp:864
Vector bb_min
Definition ex9p.cpp:66
Vector bb_max
Definition ex9p.cpp:66
constexpr ARKODE_ERKTableID ARKODE_FEHLBERG_13_7_8
Definition sundials.hpp:69