MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
ex16p.cpp
Go to the documentation of this file.
1// MFEM Example 16 - Parallel Version
2//
3// Compile with: make ex16p
4//
5// Sample runs: mpirun -np 4 ex16p
6// mpirun -np 4 ex16p -m ../data/inline-tri.mesh
7// mpirun -np 4 ex16p -m ../data/disc-nurbs.mesh -tf 2
8// mpirun -np 4 ex16p -s 21 -a 0.0 -k 1.0
9// mpirun -np 4 ex16p -s 22 -a 1.0 -k 0.0
10// mpirun -np 8 ex16p -s 23 -a 0.5 -k 0.5 -o 4
11// mpirun -np 4 ex16p -s 4 -dt 1.0e-4 -tf 4.0e-2 -vs 40
12// mpirun -np 16 ex16p -m ../data/fichera-q2.mesh
13// mpirun -np 16 ex16p -m ../data/fichera-mixed.mesh
14// mpirun -np 16 ex16p -m ../data/escher-p2.mesh
15// mpirun -np 8 ex16p -m ../data/beam-tet.mesh -tf 10 -dt 0.1
16// mpirun -np 4 ex16p -m ../data/amr-quad.mesh -o 4 -rs 0 -rp 0
17// mpirun -np 4 ex16p -m ../data/amr-hex.mesh -o 2 -rs 0 -rp 0
18// mpirun -np 4 ex16p -m ../data/amr-hex.mesh -o 2 -rs 0 -rp 0 -s 21 -imp-state
19//
20// Description: This example solves a time dependent nonlinear heat equation
21// problem of the form du/dt = C(u), with a non-linear diffusion
22// operator C(u) = \nabla \cdot (\kappa + \alpha u) \nabla u.
23//
24// The example demonstrates the use of nonlinear operators (the
25// class ConductionOperator defining C(u)), as well as their
26// implicit time integration. Note that implementing the method
27// ConductionOperator::ImplicitSolve is the only requirement for
28// high-order implicit (SDIRK) time integration. In this example,
29// the diffusion operator is linearized by evaluating with the
30// lagged solution from the previous timestep, so there is only
31// a linear solve. Optional saving with ADIOS2
32// (adios2.readthedocs.io) is also illustrated.
33//
34// We recommend viewing examples 2, 9 and 10 before viewing this
35// example.
36
37#include "mfem.hpp"
38#include <fstream>
39#include <iostream>
40
41using namespace std;
42using namespace mfem;
43
44/** After spatial discretization, the conduction model can be written as:
45 *
46 * du/dt = M^{-1}(-Ku)
47 *
48 * where u is the vector representing the temperature, M is the mass matrix,
49 * and K is the diffusion operator with diffusivity depending on u:
50 * (\kappa + \alpha u).
51 *
52 * Class ConductionOperator represents the right-hand side of the above ODE.
53 */
54class ConductionOperator : public TimeDependentOperator
55{
56protected:
57 ParFiniteElementSpace &fespace;
58 Array<int> ess_tdof_list; // this list remains empty for pure Neumann b.c.
59
62
63 HypreParMatrix Mmat;
64 HypreParMatrix Kmat;
65 HypreParMatrix *T; // T = M + dt K
66 real_t current_dt;
67
68 CGSolver M_solver; // Krylov solver for inverting the mass matrix M
69 HypreSmoother M_prec; // Preconditioner for the mass matrix M
70
71 CGSolver T_solver; // Implicit solver for T = M + dt K
72 HypreSmoother T_prec; // Preconditioner for the implicit solver
73
74 real_t alpha, kappa;
75
76 mutable Vector z; // auxiliary vector
77
78public:
79 ConductionOperator(ParFiniteElementSpace &f, real_t alpha, real_t kappa,
80 const Vector &u);
81
82 void Mult(const Vector &u, Vector &du_dt) const override;
83 /** Solve the Backward-Euler equation: k = f(u + dt*k, t), for the unknown k.
84 This is the only requirement for high-order SDIRK implicit integration.*/
85 void ImplicitSolve(const real_t dt, const Vector &u, Vector &k) override;
86
87 /// Update the diffusion BilinearForm K using the given true-dof vector `u`.
88 void SetParameters(const Vector &u);
89
90 ~ConductionOperator() override;
91};
92
94
95int main(int argc, char *argv[])
96{
97 // 1. Initialize MPI and HYPRE.
98 Mpi::Init(argc, argv);
99 int num_procs = Mpi::WorldSize();
100 int myid = Mpi::WorldRank();
101 Hypre::Init();
102
103 // 2. Parse command-line options.
104 const char *mesh_file = "../data/star.mesh";
105 int ser_ref_levels = 2;
106 int par_ref_levels = 1;
107 int order = 2;
108
109 int ode_solver_type = 23; // SDIRK33Solver
110 real_t t_final = 0.5;
111 real_t dt = 1.0e-2;
112 real_t alpha = 1.0e-2;
113 real_t kappa = 0.5;
114
115 bool visualization = true;
116 bool visit = false;
117 int vis_steps = 5;
118 bool adios2 = false;
119 bool solve_implicit_state = false;
120
121 int precision = 8;
122 cout.precision(precision);
123
124 OptionsParser args(argc, argv);
125 args.AddOption(&mesh_file, "-m", "--mesh",
126 "Mesh file to use.");
127 args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
128 "Number of times to refine the mesh uniformly in serial.");
129 args.AddOption(&par_ref_levels, "-rp", "--refine-parallel",
130 "Number of times to refine the mesh uniformly in parallel.");
131 args.AddOption(&order, "-o", "--order",
132 "Order (degree) of the finite elements.");
133 args.AddOption(&ode_solver_type, "-s", "--ode-solver",
134 ODESolver::Types.c_str());
135 args.AddOption(&t_final, "-tf", "--t-final",
136 "Final time; start time is 0.");
137 args.AddOption(&dt, "-dt", "--time-step",
138 "Time step.");
139 args.AddOption(&alpha, "-a", "--alpha",
140 "Alpha coefficient.");
141 args.AddOption(&kappa, "-k", "--kappa",
142 "Kappa coefficient offset.");
143 args.AddOption(&solve_implicit_state, "-imp-state", "--implicit-state",
144 "-imp-slope", "--implicit-slope",
145 "Implicitly solve for stage state or slope.");
146 args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
147 "--no-visualization",
148 "Enable or disable GLVis visualization.");
149 args.AddOption(&visit, "-visit", "--visit-datafiles", "-no-visit",
150 "--no-visit-datafiles",
151 "Save data files for VisIt (visit.llnl.gov) visualization.");
152 args.AddOption(&vis_steps, "-vs", "--visualization-steps",
153 "Visualize every n-th timestep.");
154 args.AddOption(&adios2, "-adios2", "--adios2-streams", "-no-adios2",
155 "--no-adios2-streams",
156 "Save data using adios2 streams.");
157 args.Parse();
158 if (!args.Good())
159 {
160 args.PrintUsage(cout);
161 return 1;
162 }
163
164 if (myid == 0)
165 {
166 args.PrintOptions(cout);
167 }
168
169 // 3. Read the serial mesh from the given mesh file on all processors. We can
170 // handle triangular, quadrilateral, tetrahedral and hexahedral meshes
171 // with the same code.
172 Mesh *mesh = new Mesh(mesh_file, 1, 1);
173 int dim = mesh->Dimension();
174
175 // 4. Define the ODE solver used for time integration. Several implicit
176 // singly diagonal implicit Runge-Kutta (SDIRK) methods, as well as
177 // explicit Runge-Kutta methods are available.
178 unique_ptr<ODESolver> ode_solver = ODESolver::Select(ode_solver_type);
179
180 // 5. Refine the mesh in serial to increase the resolution. In this example
181 // we do 'ser_ref_levels' of uniform refinement, where 'ser_ref_levels' is
182 // a command-line parameter.
183 for (int lev = 0; lev < ser_ref_levels; lev++)
184 {
185 mesh->UniformRefinement();
186 }
187
188 // 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
189 // this mesh further in parallel to increase the resolution. Once the
190 // parallel mesh is defined, the serial mesh can be deleted.
191 ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
192 delete mesh;
193 for (int lev = 0; lev < par_ref_levels; lev++)
194 {
195 pmesh->UniformRefinement();
196 }
197
198 // 7. Define the vector finite element space representing the current and the
199 // initial temperature, u_ref.
200 H1_FECollection fe_coll(order, dim);
201 ParFiniteElementSpace fespace(pmesh, &fe_coll);
202
203 HYPRE_BigInt fe_size = fespace.GlobalTrueVSize();
204 if (myid == 0)
205 {
206 cout << "Number of temperature unknowns: " << fe_size << endl;
207 }
208
209 ParGridFunction u_gf(&fespace);
210
211 // 8. Set the initial conditions for u. All boundaries are considered
212 // natural.
214 u_gf.ProjectCoefficient(u_0);
215 Vector u;
216 u_gf.GetTrueDofs(u);
217
218 // 9. Initialize the conduction operator and the VisIt visualization.
219 ConductionOperator oper(fespace, alpha, kappa, u);
220 using ImplicitVariableType = ConductionOperator::ImplicitVariableType;
221
222 u_gf.SetFromTrueDofs(u);
223 {
224 ostringstream mesh_name, sol_name;
225 mesh_name << "ex16-mesh." << setfill('0') << setw(6) << myid;
226 sol_name << "ex16-init." << setfill('0') << setw(6) << myid;
227 ofstream omesh(mesh_name.str().c_str());
228 omesh.precision(precision);
229 pmesh->Print(omesh);
230 ofstream osol(sol_name.str().c_str());
231 osol.precision(precision);
232 u_gf.Save(osol);
233 }
234
235 VisItDataCollection visit_dc("Example16-Parallel", pmesh);
236 visit_dc.RegisterField("temperature", &u_gf);
237 if (visit)
238 {
239 visit_dc.SetCycle(0);
240 visit_dc.SetTime(0.0);
241 visit_dc.Save();
242 }
243
244 // Optionally output a BP (binary pack) file using ADIOS2. This can be
245 // visualized with the ParaView VTX reader.
246#ifdef MFEM_USE_ADIOS2
247 ADIOS2DataCollection* adios2_dc = NULL;
248 if (adios2)
249 {
250 std::string postfix(mesh_file);
251 postfix.erase(0, std::string("../data/").size() );
252 postfix += "_o" + std::to_string(order);
253 postfix += "_solver" + std::to_string(ode_solver_type);
254 const std::string collection_name = "ex16-p-" + postfix + ".bp";
255
256 adios2_dc = new ADIOS2DataCollection(MPI_COMM_WORLD, collection_name, pmesh);
257 adios2_dc->SetParameter("SubStreams", std::to_string(num_procs/2) );
258 adios2_dc->RegisterField("temperature", &u_gf);
259 adios2_dc->SetCycle(0);
260 adios2_dc->SetTime(0.0);
261 adios2_dc->Save();
262 }
263#endif
264
265 socketstream sout;
266 if (visualization)
267 {
268 char vishost[] = "localhost";
269 int visport = 19916;
270 sout.open(vishost, visport);
271 sout << "parallel " << num_procs << " " << myid << endl;
272 int good = sout.good(), all_good;
273 MPI_Allreduce(&good, &all_good, 1, MPI_INT, MPI_MIN, pmesh->GetComm());
274 if (!all_good)
275 {
276 sout.close();
277 visualization = false;
278 if (myid == 0)
279 {
280 cout << "Unable to connect to GLVis server at "
281 << vishost << ':' << visport << endl;
282 cout << "GLVis visualization disabled.\n";
283 }
284 }
285 else
286 {
287 sout.precision(precision);
288 sout << "solution\n" << *pmesh << u_gf;
289 sout << "pause\n";
290 sout << flush;
291 if (myid == 0)
292 {
293 cout << "GLVis visualization paused."
294 << " Press space (in the GLVis window) to resume it.\n";
295 }
296 }
297 }
298
299 ImplicitVariableType imp_var = solve_implicit_state ?
300 ImplicitVariableType::STATE
301 : ImplicitVariableType::SLOPE;
302
303 // 10. Perform time-integration (looping over the time iterations, ti, with a
304 // time-step dt).
305 ode_solver->Init(oper);
306 ode_solver->SetImplicitVariableType(imp_var);
307 real_t t = 0.0;
308
309 bool last_step = false;
310 for (int ti = 1; !last_step; ti++)
311 {
312 if (t + dt >= t_final - dt/2)
313 {
314 last_step = true;
315 }
316
317 ode_solver->Step(u, t, dt);
318
319 if (last_step || (ti % vis_steps) == 0)
320 {
321 if (myid == 0)
322 {
323 cout << "step " << ti << ", t = " << t << endl;
324 }
325
326 u_gf.SetFromTrueDofs(u);
327 if (visualization)
328 {
329 sout << "parallel " << num_procs << " " << myid << "\n";
330 sout << "solution\n" << *pmesh << u_gf << flush;
331 }
332
333 if (visit)
334 {
335 visit_dc.SetCycle(ti);
336 visit_dc.SetTime(t);
337 visit_dc.Save();
338 }
339
340#ifdef MFEM_USE_ADIOS2
341 if (adios2)
342 {
343 adios2_dc->SetCycle(ti);
344 adios2_dc->SetTime(t);
345 adios2_dc->Save();
346 }
347#endif
348 }
349 oper.SetParameters(u);
350 }
351
352#ifdef MFEM_USE_ADIOS2
353 if (adios2)
354 {
355 delete adios2_dc;
356 }
357#endif
358
359 // 11. Save the final solution in parallel. This output can be viewed later
360 // using GLVis: "glvis -np <np> -m ex16-mesh -g ex16-final".
361 {
362 ostringstream sol_name;
363 sol_name << "ex16-final." << setfill('0') << setw(6) << myid;
364 ofstream osol(sol_name.str().c_str());
365 osol.precision(precision);
366 u_gf.Save(osol);
367 }
368
369 // 12. Free the used memory.
370 delete pmesh;
371
372 return 0;
373}
374
375ConductionOperator::ConductionOperator(ParFiniteElementSpace &f, real_t al,
376 real_t kap, const Vector &u)
377 : TimeDependentOperator(f.GetTrueVSize(), (real_t) 0.0), fespace(f),
378 M(NULL), K(NULL), T(NULL), current_dt(0.0),
379 M_solver(f.GetComm()), T_solver(f.GetComm()), z(height)
380{
381 const real_t rel_tol = 1e-8;
382
383 M = new ParBilinearForm(&fespace);
384 M->AddDomainIntegrator(new MassIntegrator());
385 M->Assemble(0); // keep sparsity pattern of M and K the same
386 M->FormSystemMatrix(ess_tdof_list, Mmat);
387
388 M_solver.iterative_mode = false;
389 M_solver.SetRelTol(rel_tol);
390 M_solver.SetAbsTol(0.0);
391 M_solver.SetMaxIter(100);
392 M_solver.SetPrintLevel(0);
393 M_prec.SetType(HypreSmoother::Jacobi);
394 M_solver.SetPreconditioner(M_prec);
395 M_solver.SetOperator(Mmat);
396
397 alpha = al;
398 kappa = kap;
399
400 T_solver.iterative_mode = false;
401 T_solver.SetRelTol(rel_tol);
402 T_solver.SetAbsTol(0.0);
403 T_solver.SetMaxIter(100);
404 T_solver.SetPrintLevel(0);
405 T_solver.SetPreconditioner(T_prec);
406
407 SetParameters(u);
408}
409
410void ConductionOperator::Mult(const Vector &u, Vector &du_dt) const
411{
412 // Compute:
413 // du_dt = M^{-1}*-Ku
414 // for du_dt, where K is linearized by using u from the previous timestep
415 Kmat.Mult(u, z);
416 z.Neg(); // z = -z
417 M_solver.Mult(z, du_dt);
418}
419
420void ConductionOperator::ImplicitSolve(const real_t dt,
421 const Vector &u, Vector &k)
422{
423 // Solve the equation:
424 // M*k = -K(u + dt*k) for k = du/dt, if solving for stage-slope
425 // or
426 // M*k = -dt*K(k) + M*u for k = u_s, if solving for stage-state
427 // where K is linearized by using u from the previous timestep, and
428 // the stage-state and slope relation: du/dt = (u_s - u)/dt.
429 if (!T)
430 {
431 T = Add(1.0, Mmat, dt, Kmat);
432 current_dt = dt;
433 T_solver.SetOperator(*T);
434 }
435 MFEM_VERIFY(dt == current_dt, ""); // SDIRK methods use the same dt
436
437 // Construct current right-hand side for stage state vs. slope solve
439 {
440 // k, on return, is the stage value u
441 Mmat.Mult(u, z);
442 }
443 else
444 {
445 // k, on return, is the stage slope du/dt
446 Kmat.Mult(u, z);
447 z.Neg();
448 }
449 T_solver.Mult(z, k);
450}
451
452void ConductionOperator::SetParameters(const Vector &u)
453{
454 ParGridFunction u_alpha_gf(&fespace);
455 u_alpha_gf.SetFromTrueDofs(u);
456 for (int i = 0; i < u_alpha_gf.Size(); i++)
457 {
458 u_alpha_gf(i) = kappa + alpha*u_alpha_gf(i);
459 }
460
461 delete K;
462 K = new ParBilinearForm(&fespace);
463
464 GridFunctionCoefficient u_coeff(&u_alpha_gf);
465
467 K->Assemble(0); // keep sparsity pattern of M and K the same
468 K->FormSystemMatrix(ess_tdof_list, Kmat);
469 delete T;
470 T = NULL; // re-compute T on the next ImplicitSolve
471}
472
473ConductionOperator::~ConductionOperator()
474{
475 delete T;
476 delete M;
477 delete K;
478}
479
481{
482 if (x.Norml2() < 0.5)
483 {
484 return 2.0;
485 }
486 else
487 {
488 return 1.0;
489 }
490}
void SetParameter(const std::string key, const std::string value) noexcept
void AddDomainIntegrator(BilinearFormIntegrator *bfi)
Adds new Domain Integrator. Assumes ownership of bfi.
void Assemble(int skip_zeros=1)
Assembles the form i.e. sums over all domain/bdr integrators.
virtual void FormSystemMatrix(const Array< int > &ess_tdof_list, OperatorHandle &A)
Form the linear system matrix A, see FormLinearSystem() for details.
Conjugate gradient method.
Definition solvers.hpp:627
void Mult(const Vector &b, Vector &x) const override
Iterative solution of the linear system using the Conjugate Gradient method.
Definition solvers.cpp:869
void SetOperator(const Operator &op) override
Set/update the solver for the given operator.
Definition solvers.hpp:640
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)
A general function coefficient.
Coefficient defined by a GridFunction. This coefficient is mesh dependent.
Arbitrary order H1-conforming (continuous) finite elements.
Definition fe_coll.hpp:291
Wrapper for hypre's ParCSR matrix class.
Definition hypre.hpp:419
Parallel smoothers in hypre.
Definition hypre.hpp:1077
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:67
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1314
void UniformRefinement(int i, const DSTable &, int *, int *, int *)
Definition mesh.cpp:12125
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).
static MFEM_EXPORT std::string Types
Definition ode.hpp:235
static MFEM_EXPORT std::unique_ptr< ODESolver > Select(const int ode_solver_type)
Definition ode.cpp:41
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.
Abstract parallel finite element space.
Definition pfespace.hpp:31
HYPRE_BigInt GlobalTrueVSize() const
Definition pfespace.hpp:361
Class for parallel grid function.
Definition pgridfunc.hpp:50
void Save(std::ostream &out) const override
void ProjectCoefficient(Coefficient &coeff, ProjectType type=ProjectType::DEFAULT) override
Project coeff Coefficient to this GridFunction. The projection computation depends on the choice of t...
HypreParVector * GetTrueDofs() const
Returns the true dofs in a new HypreParVector.
void SetFromTrueDofs(const Vector &tv) override
Set the GridFunction from the given true-dof vector.
Class for parallel meshes.
Definition pmesh.hpp:35
MPI_Comm GetComm() const
Definition pmesh.hpp:403
void Print(std::ostream &out=mfem::out, const std::string &comments="") const override
Definition pmesh.cpp:4856
void Mult(const Vector &x, Vector &y) const override
Matrix vector multiplication.
Base abstract class for first order time dependent operators.
Definition operator.hpp:367
virtual bool ImplicitVarTypeIsState() const
Returns true if implicit variable is STATE and false otherwise. Used by ODESolver to identify the sta...
Definition operator.hpp:484
Vector data type.
Definition vector.hpp:82
void Neg()
(*this) = -(*this)
Definition vector.cpp:376
real_t Norml2() const
Returns the l2 norm of the vector.
Definition vector.cpp:968
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.
int open(const char hostname[], int port)
Open the socket stream on 'port' at 'hostname'.
int close()
Close the socketstream.
const int * ess_tdof_list
const real_t alpha
Definition ex15.cpp:369
real_t InitialTemperature(const Vector &x)
Definition ex16p.cpp:480
real_t kappa
Definition ex24.cpp:54
int dim
Definition ex24.cpp:53
int main()
HYPRE_Int HYPRE_BigInt
int GetTrueVSize(const FieldDescriptor &f)
Get the true dof size of a field descriptor.
Definition util.hpp:786
real_t u(const Vector &xvec)
Definition lor_mms.hpp:22
float real_t
Definition config.hpp:46
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[]
STL namespace.