MFEM v4.8.0
Finite element discretization library
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
ex10p.cpp
Go to the documentation of this file.
1// MFEM Example 10 - Parallel Version
2// PETSc Modification
3//
4// Compile with: make ex10p
5//
6// Sample runs:
7// mpirun -np 4 ex10p -m ../../data/beam-quad.mesh --petscopts rc_ex10p -s 3 -rs 2 -dt 3
8// mpirun -np 4 ex10p -m ../../data/beam-quad-amr.mesh --petscopts rc_ex10p -s 3 -rs 2 -dt 3
9//
10// Description: This examples solves a time dependent nonlinear elasticity
11// problem of the form dv/dt = H(x) + S v, dx/dt = v, where H is a
12// hyperelastic model and S is a viscosity operator of Laplacian
13// type. The geometry of the domain is assumed to be as follows:
14//
15// +---------------------+
16// boundary --->| |
17// attribute 1 | |
18// (fixed) +---------------------+
19//
20// The example demonstrates the use of nonlinear operators (the
21// class HyperelasticOperator defining H(x)), as well as their
22// implicit time integration using a Newton method for solving an
23// associated reduced backward-Euler type nonlinear equation
24// (class ReducedSystemOperator). Each Newton step requires the
25// inversion of a Jacobian matrix, which is done through a
26// (preconditioned) inner solver. Note that implementing the
27// method HyperelasticOperator::ImplicitSolve is the only
28// requirement for high-order implicit (SDIRK) time integration.
29// If using PETSc to solve the nonlinear problem, use the option
30// files provided (see rc_ex10p, rc_ex10p_mf, rc_ex10p_mfop) that
31// customize the Newton-Krylov method.
32// When option --jfnk is used, PETSc will use a Jacobian-free
33// Newton-Krylov method, using a user-defined preconditioner
34// constructed with the PetscPreconditionerFactory class.
35//
36// We recommend viewing examples 2 and 9 before viewing this
37// example.
38
39#include "mfem.hpp"
40#include <memory>
41#include <iostream>
42#include <fstream>
43
44#ifndef MFEM_USE_PETSC
45#error This example requires that MFEM is built with MFEM_USE_PETSC=YES
46#endif
47
48using namespace std;
49using namespace mfem;
50
51class ReducedSystemOperator;
52
53/** After spatial discretization, the hyperelastic model can be written as a
54 * system of ODEs:
55 * dv/dt = -M^{-1}*(H(x) + S*v)
56 * dx/dt = v,
57 * where x is the vector representing the deformation, v is the velocity field,
58 * M is the mass matrix, S is the viscosity matrix, and H(x) is the nonlinear
59 * hyperelastic operator.
60 *
61 * Class HyperelasticOperator represents the right-hand side of the above
62 * system of ODEs. */
63class HyperelasticOperator : public TimeDependentOperator
64{
65protected:
66 ParFiniteElementSpace &fespace;
67 Array<int> ess_tdof_list;
68
69 ParBilinearForm M, S;
71 real_t viscosity;
72 HyperelasticModel *model;
73
74 HypreParMatrix *Mmat; // Mass matrix from ParallelAssemble()
75 CGSolver M_solver; // Krylov solver for inverting the mass matrix M
76 HypreSmoother M_prec; // Preconditioner for the mass matrix M
77
78 /** Nonlinear operator defining the reduced backward Euler equation for the
79 velocity. Used in the implementation of method ImplicitSolve. */
80 ReducedSystemOperator *reduced_oper;
81
82 /// Newton solver for the reduced backward Euler equation
83 NewtonSolver newton_solver;
84
85 /// Newton solver for the reduced backward Euler equation (PETSc SNES)
86 PetscNonlinearSolver* pnewton_solver;
87
88 /// Solver for the Jacobian solve in the Newton method
89 Solver *J_solver;
90 /// Preconditioner for the Jacobian solve in the Newton method
91 Solver *J_prec;
92 /// Preconditioner factory for JFNK
94
95 mutable Vector z; // auxiliary vector
96
97public:
98 HyperelasticOperator(ParFiniteElementSpace &f, Array<int> &ess_bdr,
99 real_t visc, real_t mu, real_t K,
100 bool use_petsc, bool petsc_use_jfnk);
101
102 /// Compute the right-hand side of the ODE system.
103 virtual void Mult(const Vector &vx, Vector &dvx_dt) const;
104 /** Solve the Backward-Euler equation: k = f(x + dt*k, t), for the unknown k.
105 This is the only requirement for high-order SDIRK implicit integration.*/
106 virtual void ImplicitSolve(const real_t dt, const Vector &x, Vector &k);
107
108 real_t ElasticEnergy(const ParGridFunction &x) const;
109 real_t KineticEnergy(const ParGridFunction &v) const;
110 void GetElasticEnergyDensity(const ParGridFunction &x,
111 ParGridFunction &w) const;
112
113 virtual ~HyperelasticOperator();
114};
115
116/** Nonlinear operator of the form:
117 k --> (M + dt*S)*k + H(x + dt*v + dt^2*k) + S*v,
118 where M and S are given BilinearForms, H is a given NonlinearForm, v and x
119 are given vectors, and dt is a scalar. */
120class ReducedSystemOperator : public Operator
121{
122private:
123 ParBilinearForm *M, *S;
125 mutable HypreParMatrix *Jacobian;
126 real_t dt;
127 const Vector *v, *x;
128 mutable Vector w, z;
129 const Array<int> &ess_tdof_list;
130
131public:
132 ReducedSystemOperator(ParBilinearForm *M_, ParBilinearForm *S_,
133 ParNonlinearForm *H_, const Array<int> &ess_tdof_list);
134
135 /// Set current dt, v, x values - needed to compute action and Jacobian.
136 void SetParameters(real_t dt_, const Vector *v_, const Vector *x_);
137
138 /// Compute y = H(x + dt (v + dt k)) + M k + S (v + dt k).
139 virtual void Mult(const Vector &k, Vector &y) const;
140
141 /// Compute J = M + dt S + dt^2 grad_H(x + dt (v + dt k)).
142 virtual Operator &GetGradient(const Vector &k) const;
143
144 virtual ~ReducedSystemOperator();
145
146};
147
148/** Auxiliary class to provide preconditioners for matrix-free methods */
149class PreconditionerFactory : public PetscPreconditionerFactory
150{
151private:
152 // const ReducedSystemOperator& op; // unused for now (generates warning)
153
154public:
155 PreconditionerFactory(const ReducedSystemOperator& op_, const string& name_)
156 : PetscPreconditionerFactory(name_) /* , op(op_) */ {}
157 virtual mfem::Solver* NewPreconditioner(const mfem::OperatorHandle&);
158 virtual ~PreconditionerFactory() {}
159};
160
161/** Function representing the elastic energy density for the given hyperelastic
162 model+deformation. Used in HyperelasticOperator::GetElasticEnergyDensity. */
163class ElasticEnergyCoefficient : public Coefficient
164{
165private:
166 HyperelasticModel &model;
167 const ParGridFunction &x;
168 DenseMatrix J;
169
170public:
171 ElasticEnergyCoefficient(HyperelasticModel &m, const ParGridFunction &x_)
172 : model(m), x(x_) { }
173 virtual real_t Eval(ElementTransformation &T, const IntegrationPoint &ip);
174 virtual ~ElasticEnergyCoefficient() { }
175};
176
177void InitialDeformation(const Vector &x, Vector &y);
178
179void InitialVelocity(const Vector &x, Vector &v);
180
181void visualize(ostream &os, ParMesh *mesh, ParGridFunction *deformed_nodes,
182 ParGridFunction *field, const char *field_name = NULL,
183 bool init_vis = false);
184
185
186int main(int argc, char *argv[])
187{
188 // 1. Initialize MPI and HYPRE.
189 Mpi::Init(argc, argv);
190 int myid = Mpi::WorldRank();
191 Hypre::Init();
192
193 // 2. Parse command-line options.
194 const char *mesh_file = "../../data/beam-quad.mesh";
195 int ser_ref_levels = 2;
196 int par_ref_levels = 0;
197 int order = 2;
198 int ode_solver_type = 3;
199 real_t t_final = 300.0;
200 real_t dt = 3.0;
201 real_t visc = 1e-2;
202 real_t mu = 0.25;
203 real_t K = 5.0;
204 bool visualization = true;
205 int vis_steps = 1;
206 bool use_petsc = true;
207 const char *petscrc_file = "";
208 bool petsc_use_jfnk = false;
209 const char *device_config = "cpu";
210
211 OptionsParser args(argc, argv);
212 args.AddOption(&mesh_file, "-m", "--mesh",
213 "Mesh file to use.");
214 args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
215 "Number of times to refine the mesh uniformly in serial.");
216 args.AddOption(&par_ref_levels, "-rp", "--refine-parallel",
217 "Number of times to refine the mesh uniformly in parallel.");
218 args.AddOption(&order, "-o", "--order",
219 "Order (degree) of the finite elements.");
220 args.AddOption(&ode_solver_type, "-s", "--ode-solver",
221 "ODE solver: 1 - Backward Euler, 2 - SDIRK2, 3 - SDIRK3,\n\t"
222 " 11 - Forward Euler, 12 - RK2,\n\t"
223 " 13 - RK3 SSP, 14 - RK4.");
224 args.AddOption(&t_final, "-tf", "--t-final",
225 "Final time; start time is 0.");
226 args.AddOption(&dt, "-dt", "--time-step",
227 "Time step.");
228 args.AddOption(&visc, "-v", "--viscosity",
229 "Viscosity coefficient.");
230 args.AddOption(&mu, "-mu", "--shear-modulus",
231 "Shear modulus in the Neo-Hookean hyperelastic model.");
232 args.AddOption(&K, "-K", "--bulk-modulus",
233 "Bulk modulus in the Neo-Hookean hyperelastic model.");
234 args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
235 "--no-visualization",
236 "Enable or disable GLVis visualization.");
237 args.AddOption(&vis_steps, "-vs", "--visualization-steps",
238 "Visualize every n-th timestep.");
239 args.AddOption(&use_petsc, "-usepetsc", "--usepetsc", "-no-petsc",
240 "--no-petsc",
241 "Use or not PETSc to solve the nonlinear system.");
242 args.AddOption(&petscrc_file, "-petscopts", "--petscopts",
243 "PetscOptions file to use.");
244 args.AddOption(&petsc_use_jfnk, "-jfnk", "--jfnk", "-no-jfnk",
245 "--no-jfnk",
246 "Use JFNK with user-defined preconditioner factory.");
247 args.AddOption(&device_config, "-d", "--device",
248 "Device configuration string, see Device::Configure().");
249 args.Parse();
250 if (!args.Good())
251 {
252 if (myid == 0)
253 {
254 args.PrintUsage(cout);
255 }
256 return 1;
257 }
258 if (myid == 0)
259 {
260 args.PrintOptions(cout);
261 }
262
263 // 2b. Enable hardware devices such as GPUs, and programming models such as
264 // CUDA, OCCA, RAJA and OpenMP based on command line options.
265 Device device(device_config);
266 if (myid == 0) { device.Print(); }
267
268 // 2c. We initialize PETSc
269 if (use_petsc)
270 {
271 MFEMInitializePetsc(NULL,NULL,petscrc_file,NULL);
272 }
273
274 // 3. Read the serial mesh from the given mesh file on all processors. We can
275 // handle triangular, quadrilateral, tetrahedral and hexahedral meshes
276 // with the same code.
277 Mesh *mesh = new Mesh(mesh_file, 1, 1);
278 int dim = mesh->Dimension();
279
280 // 4. Define the ODE solver used for time integration. Several implicit
281 // singly diagonal implicit Runge-Kutta (SDIRK) methods, as well as
282 // explicit Runge-Kutta methods are available.
283 ODESolver *ode_solver;
284 switch (ode_solver_type)
285 {
286 // Implicit L-stable methods
287 case 1: ode_solver = new BackwardEulerSolver; break;
288 case 2: ode_solver = new SDIRK23Solver(2); break;
289 case 3: ode_solver = new SDIRK33Solver; break;
290 // Explicit methods
291 case 11: ode_solver = new ForwardEulerSolver; break;
292 case 12: ode_solver = new RK2Solver(0.5); break; // midpoint method
293 case 13: ode_solver = new RK3SSPSolver; break;
294 case 14: ode_solver = new RK4Solver; break;
295 // Implicit A-stable methods (not L-stable)
296 case 22: ode_solver = new ImplicitMidpointSolver; break;
297 case 23: ode_solver = new SDIRK23Solver; break;
298 case 24: ode_solver = new SDIRK34Solver; break;
299 default:
300 if (myid == 0)
301 {
302 cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
303 }
304 return 3;
305 }
306
307 // 5. Refine the mesh in serial to increase the resolution. In this example
308 // we do 'ser_ref_levels' of uniform refinement, where 'ser_ref_levels' is
309 // a command-line parameter.
310 for (int lev = 0; lev < ser_ref_levels; lev++)
311 {
312 mesh->UniformRefinement();
313 }
314
315 // 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
316 // this mesh further in parallel to increase the resolution. Once the
317 // parallel mesh is defined, the serial mesh can be deleted.
318 ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
319 delete mesh;
320 for (int lev = 0; lev < par_ref_levels; lev++)
321 {
322 pmesh->UniformRefinement();
323 }
324
325 // 7. Define the parallel vector finite element spaces representing the mesh
326 // deformation x_gf, the velocity v_gf, and the initial configuration,
327 // x_ref. Define also the elastic energy density, w_gf, which is in a
328 // discontinuous higher-order space. Since x and v are integrated in time
329 // as a system, we group them together in block vector vx, on the unique
330 // parallel degrees of freedom, with offsets given by array true_offset.
331 H1_FECollection fe_coll(order, dim);
332 ParFiniteElementSpace fespace(pmesh, &fe_coll, dim);
333
334 HYPRE_BigInt glob_size = fespace.GlobalTrueVSize();
335 if (myid == 0)
336 {
337 cout << "Number of velocity/deformation unknowns: " << glob_size << endl;
338 }
339 int true_size = fespace.TrueVSize();
340 Array<int> true_offset(3);
341 true_offset[0] = 0;
342 true_offset[1] = true_size;
343 true_offset[2] = 2*true_size;
344
345 BlockVector vx(true_offset);
346 ParGridFunction v_gf, x_gf;
347 v_gf.MakeTRef(&fespace, vx, true_offset[0]);
348 x_gf.MakeTRef(&fespace, vx, true_offset[1]);
349
350 ParGridFunction x_ref(&fespace);
351 pmesh->GetNodes(x_ref);
352
353 L2_FECollection w_fec(order + 1, dim);
354 ParFiniteElementSpace w_fespace(pmesh, &w_fec);
355 ParGridFunction w_gf(&w_fespace);
356
357 // 8. Set the initial conditions for v_gf, x_gf and vx, and define the
358 // boundary conditions on a beam-like mesh (see description above).
360 v_gf.ProjectCoefficient(velo);
361 v_gf.SetTrueVector();
363 x_gf.ProjectCoefficient(deform);
364 x_gf.SetTrueVector();
365
367
368 Array<int> ess_bdr(fespace.GetMesh()->bdr_attributes.Max());
369 ess_bdr = 0;
370 ess_bdr[0] = 1; // boundary attribute 1 (index 0) is fixed
371
372 // 9. Initialize the hyperelastic operator, the GLVis visualization and print
373 // the initial energies.
374 HyperelasticOperator *oper = new HyperelasticOperator(fespace, ess_bdr, visc,
375 mu, K, use_petsc,
376 petsc_use_jfnk);
377
378 socketstream vis_v, vis_w;
379 if (visualization)
380 {
381 char vishost[] = "localhost";
382 int visport = 19916;
383 vis_v.open(vishost, visport);
384 vis_v.precision(8);
385 visualize(vis_v, pmesh, &x_gf, &v_gf, "Velocity", true);
386 // Make sure all ranks have sent their 'v' solution before initiating
387 // another set of GLVis connections (one from each rank):
388 MPI_Barrier(pmesh->GetComm());
389 vis_w.open(vishost, visport);
390 if (vis_w)
391 {
392 oper->GetElasticEnergyDensity(x_gf, w_gf);
393 vis_w.precision(8);
394 visualize(vis_w, pmesh, &x_gf, &w_gf, "Elastic energy density", true);
395 }
396 }
397
398 real_t ee0 = oper->ElasticEnergy(x_gf);
399 real_t ke0 = oper->KineticEnergy(v_gf);
400 if (myid == 0)
401 {
402 cout << "initial elastic energy (EE) = " << ee0 << endl;
403 cout << "initial kinetic energy (KE) = " << ke0 << endl;
404 cout << "initial total energy (TE) = " << (ee0 + ke0) << endl;
405 }
406
407 real_t t = 0.0;
408 oper->SetTime(t);
409 ode_solver->Init(*oper);
410
411 // 10. Perform time-integration
412 // (looping over the time iterations, ti, with a time-step dt).
413 bool last_step = false;
414 for (int ti = 1; !last_step; ti++)
415 {
416 real_t dt_real = min(dt, t_final - t);
417
418 ode_solver->Step(vx, t, dt_real);
419
420 last_step = (t >= t_final - 1e-8*dt);
421
422 if (last_step || (ti % vis_steps) == 0)
423 {
425
426 real_t ee = oper->ElasticEnergy(x_gf);
427 real_t ke = oper->KineticEnergy(v_gf);
428
429 if (myid == 0)
430 {
431 cout << "step " << ti << ", t = " << t << ", EE = " << ee
432 << ", KE = " << ke << ", ΔTE = " << (ee+ke)-(ee0+ke0) << endl;
433 }
434
435 if (visualization)
436 {
437 visualize(vis_v, pmesh, &x_gf, &v_gf);
438 if (vis_w)
439 {
440 oper->GetElasticEnergyDensity(x_gf, w_gf);
441 visualize(vis_w, pmesh, &x_gf, &w_gf);
442 }
443 }
444 }
445 }
446
447 // 11. Save the displaced mesh, the velocity and elastic energy.
448 {
450 GridFunction *nodes = &x_gf;
451 int owns_nodes = 0;
452 pmesh->SwapNodes(nodes, owns_nodes);
453
454 ostringstream mesh_name, velo_name, ee_name;
455 mesh_name << "deformed." << setfill('0') << setw(6) << myid;
456 velo_name << "velocity." << setfill('0') << setw(6) << myid;
457 ee_name << "elastic_energy." << setfill('0') << setw(6) << myid;
458
459 ofstream mesh_ofs(mesh_name.str().c_str());
460 mesh_ofs.precision(8);
461 pmesh->Print(mesh_ofs);
462 pmesh->SwapNodes(nodes, owns_nodes);
463 ofstream velo_ofs(velo_name.str().c_str());
464 velo_ofs.precision(8);
465 v_gf.Save(velo_ofs);
466 ofstream ee_ofs(ee_name.str().c_str());
467 ee_ofs.precision(8);
468 oper->GetElasticEnergyDensity(x_gf, w_gf);
469 w_gf.Save(ee_ofs);
470 }
471
472 // 12. Free the used memory.
473 delete ode_solver;
474 delete pmesh;
475 delete oper;
476
477 // We finalize PETSc
478 if (use_petsc) { MFEMFinalizePetsc(); }
479
480 return 0;
481}
482
483void visualize(ostream &os, ParMesh *mesh, ParGridFunction *deformed_nodes,
484 ParGridFunction *field, const char *field_name, bool init_vis)
485{
486 if (!os)
487 {
488 return;
489 }
490
491 GridFunction *nodes = deformed_nodes;
492 int owns_nodes = 0;
493
494 mesh->SwapNodes(nodes, owns_nodes);
495
496 os << "parallel " << mesh->GetNRanks() << " " << mesh->GetMyRank() << "\n";
497 os << "solution\n" << *mesh << *field;
498
499 mesh->SwapNodes(nodes, owns_nodes);
500
501 if (init_vis)
502 {
503 os << "window_size 800 800\n";
504 os << "window_title '" << field_name << "'\n";
505 if (mesh->SpaceDimension() == 2)
506 {
507 os << "view 0 0\n"; // view from top
508 os << "keys jl\n"; // turn off perspective and light
509 }
510 os << "keys cm\n"; // show colorbar and mesh
511 os << "autoscale value\n"; // update value-range; keep mesh-extents fixed
512 os << "pause\n";
513 }
514 os << flush;
515}
516
517
518ReducedSystemOperator::ReducedSystemOperator(
520 const Array<int> &ess_tdof_list_)
521 : Operator(M_->ParFESpace()->TrueVSize()), M(M_), S(S_), H(H_),
522 Jacobian(NULL), dt(0.0), v(NULL), x(NULL), w(height), z(height),
523 ess_tdof_list(ess_tdof_list_)
524{ }
525
526void ReducedSystemOperator::SetParameters(real_t dt_, const Vector *v_,
527 const Vector *x_)
528{
529 dt = dt_; v = v_; x = x_;
530}
531
532void ReducedSystemOperator::Mult(const Vector &k, Vector &y) const
533{
534 // compute: y = H(x + dt*(v + dt*k)) + M*k + S*(v + dt*k)
535 add(*v, dt, k, w);
536 add(*x, dt, w, z);
537 H->Mult(z, y);
538 M->TrueAddMult(k, y);
539 S->TrueAddMult(w, y);
540 y.SetSubVector(ess_tdof_list, 0.0);
541}
542
543Operator &ReducedSystemOperator::GetGradient(const Vector &k) const
544{
545 delete Jacobian;
546 SparseMatrix *localJ = Add(1.0, M->SpMat(), dt, S->SpMat());
547 add(*v, dt, k, w);
548 add(*x, dt, w, z);
549 localJ->Add(dt*dt, H->GetLocalGradient(z));
550 // if we are using PETSc, the HypreParCSR Jacobian will be converted to
551 // PETSc's AIJ on the fly
552 Jacobian = M->ParallelAssemble(localJ);
553 delete localJ;
554 HypreParMatrix *Je = Jacobian->EliminateRowsCols(ess_tdof_list);
555 delete Je;
556 return *Jacobian;
557}
558
559ReducedSystemOperator::~ReducedSystemOperator()
560{
561 delete Jacobian;
562}
563
564
565HyperelasticOperator::HyperelasticOperator(ParFiniteElementSpace &f,
566 Array<int> &ess_bdr, real_t visc,
567 real_t mu, real_t K, bool use_petsc,
568 bool use_petsc_factory)
569 : TimeDependentOperator(2*f.TrueVSize(), static_cast<real_t>(0.0)), fespace(f),
570 M(&fespace), S(&fespace), H(&fespace),
571 viscosity(visc), M_solver(f.GetComm()),
572 newton_solver(f.GetComm()), pnewton_solver(NULL), z(height/2)
573{
574#if defined(MFEM_USE_DOUBLE)
575 const real_t rel_tol = 1e-8;
576 const real_t newton_abs_tol = 0.0;
577#elif defined(MFEM_USE_SINGLE)
578 const real_t rel_tol = 1e-3;
579 const real_t newton_abs_tol = 1e-4;
580#endif
581 const int skip_zero_entries = 0;
582
583 const real_t ref_density = 1.0; // density in the reference configuration
584 ConstantCoefficient rho0(ref_density);
585 M.AddDomainIntegrator(new VectorMassIntegrator(rho0));
586 M.Assemble(skip_zero_entries);
587 M.Finalize(skip_zero_entries);
588 Mmat = M.ParallelAssemble();
589 fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
590 HypreParMatrix *Me = Mmat->EliminateRowsCols(ess_tdof_list);
591 delete Me;
592
593 M_solver.iterative_mode = false;
594 M_solver.SetRelTol(rel_tol);
595 M_solver.SetAbsTol(0.0);
596 M_solver.SetMaxIter(30);
597 M_solver.SetPrintLevel(0);
598 M_prec.SetType(HypreSmoother::Jacobi);
599 M_solver.SetPreconditioner(M_prec);
600 M_solver.SetOperator(*Mmat);
601
602 model = new NeoHookeanModel(mu, K);
603 H.AddDomainIntegrator(new HyperelasticNLFIntegrator(model));
604 H.SetEssentialTrueDofs(ess_tdof_list);
605
606 ConstantCoefficient visc_coeff(viscosity);
607 S.AddDomainIntegrator(new VectorDiffusionIntegrator(visc_coeff));
608 S.Assemble(skip_zero_entries);
609 S.Finalize(skip_zero_entries);
610
611 reduced_oper = new ReducedSystemOperator(&M, &S, &H, ess_tdof_list);
612 if (!use_petsc)
613 {
614 HypreSmoother *J_hypreSmoother = new HypreSmoother;
615 J_hypreSmoother->SetType(HypreSmoother::l1Jacobi);
616 J_hypreSmoother->SetPositiveDiagonal(true);
617 J_prec = J_hypreSmoother;
618
619 MINRESSolver *J_minres = new MINRESSolver(f.GetComm());
620 J_minres->SetRelTol(rel_tol);
621 J_minres->SetAbsTol(0.0);
622 J_minres->SetMaxIter(300);
623 J_minres->SetPrintLevel(-1);
624 J_minres->SetPreconditioner(*J_prec);
625 J_solver = J_minres;
626
627 J_factory = NULL;
628
629 newton_solver.iterative_mode = false;
630 newton_solver.SetSolver(*J_solver);
631 newton_solver.SetOperator(*reduced_oper);
632 newton_solver.SetPrintLevel(1); // print Newton iterations
633 newton_solver.SetRelTol(rel_tol);
634 newton_solver.SetAbsTol(newton_abs_tol);
635 newton_solver.SetMaxIter(10);
636 }
637 else
638 {
639 // if using PETSc, we create the same solver (Newton + MINRES + Jacobi)
640 // by command line options (see rc_ex10p)
641 J_solver = NULL;
642 J_prec = NULL;
643 J_factory = NULL;
644 pnewton_solver = new PetscNonlinearSolver(f.GetComm(),
645 *reduced_oper);
646
647 // we can setup a factory to construct a "physics-based" preconditioner
648 if (use_petsc_factory)
649 {
650 J_factory = new PreconditionerFactory(*reduced_oper, "JFNK preconditioner");
651 pnewton_solver->SetPreconditionerFactory(J_factory);
652 }
653 pnewton_solver->SetPrintLevel(1); // print Newton iterations
654 pnewton_solver->SetRelTol(rel_tol);
655 pnewton_solver->SetAbsTol(newton_abs_tol);
656 pnewton_solver->SetMaxIter(10);
657 }
658}
659
660void HyperelasticOperator::Mult(const Vector &vx, Vector &dvx_dt) const
661{
662 // Create views to the sub-vectors v, x of vx, and dv_dt, dx_dt of dvx_dt
663 int sc = height/2;
664 Vector v(vx.GetData() + 0, sc);
665 Vector x(vx.GetData() + sc, sc);
666 Vector dv_dt(dvx_dt.GetData() + 0, sc);
667 Vector dx_dt(dvx_dt.GetData() + sc, sc);
668
669 H.Mult(x, z);
670 if (viscosity != 0.0)
671 {
672 S.TrueAddMult(v, z);
673 z.SetSubVector(ess_tdof_list, 0.0);
674 }
675 z.Neg(); // z = -z
676 M_solver.Mult(z, dv_dt);
677
678 dx_dt = v;
679}
680
681void HyperelasticOperator::ImplicitSolve(const real_t dt,
682 const Vector &vx, Vector &dvx_dt)
683{
684 int sc = height/2;
685 Vector v(vx.GetData() + 0, sc);
686 Vector x(vx.GetData() + sc, sc);
687 Vector dv_dt(dvx_dt.GetData() + 0, sc);
688 Vector dx_dt(dvx_dt.GetData() + sc, sc);
689
690 // By eliminating kx from the coupled system:
691 // kv = -M^{-1}*[H(x + dt*kx) + S*(v + dt*kv)]
692 // kx = v + dt*kv
693 // we reduce it to a nonlinear equation for kv, represented by the
694 // reduced_oper. This equation is solved with the newton_solver
695 // object (using J_solver and J_prec internally).
696 reduced_oper->SetParameters(dt, &v, &x);
697 Vector zero; // empty vector is interpreted as zero r.h.s. by NewtonSolver
698 if (!pnewton_solver)
699 {
700 newton_solver.Mult(zero, dv_dt);
701 MFEM_VERIFY(newton_solver.GetConverged(),
702 "Newton solver did not converge.");
703 }
704 else
705 {
706 pnewton_solver->Mult(zero, dv_dt);
707 MFEM_VERIFY(pnewton_solver->GetConverged(),
708 "Newton solver did not converge.");
709 }
710 add(v, dt, dv_dt, dx_dt);
711}
712
713real_t HyperelasticOperator::ElasticEnergy(const ParGridFunction &x) const
714{
715 return H.GetEnergy(x);
716}
717
718real_t HyperelasticOperator::KineticEnergy(const ParGridFunction &v) const
719{
720 real_t energy = 0.5*M.ParInnerProduct(v, v);
721 return energy;
722}
723
724void HyperelasticOperator::GetElasticEnergyDensity(
725 const ParGridFunction &x, ParGridFunction &w) const
726{
727 ElasticEnergyCoefficient w_coeff(*model, x);
728 w.ProjectCoefficient(w_coeff);
729}
730
731HyperelasticOperator::~HyperelasticOperator()
732{
733 delete J_solver;
734 delete J_prec;
735 delete J_factory;
736 delete reduced_oper;
737 delete model;
738 delete Mmat;
739 delete pnewton_solver;
740}
741
742// This method gets called every time we need a preconditioner "oh"
743// contains the PetscParMatrix that wraps the operator constructed in
744// the GetGradient() method (see also PetscSolver::SetJacobianType()).
745// In this example, we just return a customizable PetscPreconditioner
746// using that matrix. However, the OperatorHandle argument can be
747// ignored, and any "physics-based" solver can be constructed since we
748// have access to the HyperElasticOperator class.
749Solver* PreconditionerFactory::NewPreconditioner(const mfem::OperatorHandle& oh)
750{
751 PetscParMatrix *pP;
752 oh.Get(pP);
753 return new PetscPreconditioner(*pP,"jfnk_");
754}
755
756real_t ElasticEnergyCoefficient::Eval(ElementTransformation &T,
757 const IntegrationPoint &ip)
758{
759 model.SetTransformation(T);
760 x.GetVectorGradient(T, J);
761 // return model.EvalW(J); // in reference configuration
762 return model.EvalW(J)/J.Det(); // in deformed configuration
763}
764
765
767{
768 // set the initial configuration to be the same as the reference,
769 // stress free, configuration
770 y = x;
771}
772
773void InitialVelocity(const Vector &x, Vector &v)
774{
775 const int dim = x.Size();
776 const real_t s = 0.1/64.;
777
778 v = 0.0;
779 v(dim-1) = s*x(0)*x(0)*(8.0-x(0));
780 v(0) = -s*x(0)*x(0);
781}
T Max() const
Find the maximal element in the array, using the comparison operator < for class T.
Definition array.cpp:68
Backward Euler ODE solver. L-stable.
Definition ode.hpp:337
const SparseMatrix & SpMat() const
Returns a const reference to the sparse matrix: .
A class to handle Vectors in a block fashion.
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
Base class Coefficients that optionally depend on space and time. These are used by the BilinearFormI...
A coefficient that is constant across space and time.
Data type dense matrix using column-major storage.
Definition densemat.hpp:24
real_t Det() const
Definition densemat.cpp:516
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
Mesh * GetMesh() const
Returns the mesh.
Definition fespace.hpp:679
The classical forward Euler method.
Definition ode.hpp:226
Class for grid function - Vector with associated FE space.
Definition gridfunc.hpp:31
void GetVectorGradient(ElementTransformation &tr, DenseMatrix &grad) const
Compute the vector gradient with respect to the physical element variable.
void SetTrueVector()
Shortcut for calling GetTrueDofs() with GetTrueVector() as argument.
Definition gridfunc.hpp:147
void MakeTRef(FiniteElementSpace *f, real_t *tv)
Associate a new FiniteElementSpace and new true-dof data with the GridFunction.
Definition gridfunc.cpp:251
void SetFromTrueVector()
Shortcut for calling SetFromTrueDofs() with GetTrueVector() as argument.
Definition gridfunc.hpp:153
Arbitrary order H1-conforming (continuous) finite elements.
Definition fe_coll.hpp:275
Abstract class for hyperelastic models.
virtual real_t EvalW(const DenseMatrix &Jpt) const =0
Evaluate the strain energy density function, W = W(Jpt).
void SetTransformation(ElementTransformation &Ttr_)
Wrapper for hypre's ParCSR matrix class.
Definition hypre.hpp:408
void EliminateRowsCols(const Array< int > &rows_cols, const HypreParVector &X, HypreParVector &B)
Definition hypre.cpp:2397
Parallel smoothers in hypre.
Definition hypre.hpp:1046
void SetPositiveDiagonal(bool pos=true)
After computing l1-norms, replace them with their absolute values.
Definition hypre.hpp:1162
void SetType(HypreSmoother::Type type, int relax_times=1)
Set the relaxation type and number of sweeps.
Definition hypre.cpp:3608
@ l1Jacobi
l1-scaled Jacobi
Definition hypre.hpp:1106
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
Class for integration point with weight.
Definition intrules.hpp:35
void SetRelTol(real_t rtol)
Definition solvers.hpp:229
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
bool GetConverged() const
Returns true if the last call to Mult() converged successfully.
Definition solvers.hpp:282
void SetAbsTol(real_t atol)
Definition solvers.hpp:230
Arbitrary order "L2-conforming" discontinuous finite elements.
Definition fe_coll.hpp:346
MINRES method.
Definition solvers.hpp:653
void SetPreconditioner(Solver &pr) override
This should be called before SetOperator.
Definition solvers.hpp:665
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
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1216
int SpaceDimension() const
Dimension of the physical space containing the mesh.
Definition mesh.hpp:1219
void GetNodes(Vector &node_coord) const
Definition mesh.cpp:9294
void UniformRefinement(int i, const DSTable &, int *, int *, int *)
Definition mesh.cpp:11295
void SwapNodes(GridFunction *&nodes, int &own_nodes_)
Swap the internal node GridFunction pointer and ownership flag members with the given ones.
Definition mesh.cpp:9343
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).
Newton's method for solving F(x)=b for a given operator F.
Definition solvers.hpp:692
void Mult(const Vector &b, Vector &x) const override
Solve the nonlinear system with right-hand side b.
Definition solvers.cpp:1912
virtual real_t GetEnergy(const Vector &x) const
Compute the energy corresponding to the state x.
void Mult(const Vector &x, Vector &y) const override
Evaluate the action of the NonlinearForm.
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
void Get(OpType *&A) const
Return the Operator pointer statically cast to a given OpType.
Definition handle.hpp:114
Abstract operator.
Definition operator.hpp:25
int height
Dimension of the output / number of rows in the matrix.
Definition operator.hpp:27
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:29
HYPRE_BigInt GlobalTrueVSize() const
Definition pfespace.hpp:346
int TrueVSize() const
Obsolete, kept for backward compatibility.
Definition pfespace.hpp:524
Class for parallel grid function.
Definition pgridfunc.hpp:50
void Save(std::ostream &out) const override
void ProjectCoefficient(Coefficient &coeff) override
Project coeff Coefficient to this GridFunction. The projection computation depends on the choice of t...
Class for parallel meshes.
Definition pmesh.hpp:34
MPI_Comm GetComm() const
Definition pmesh.hpp:402
int GetMyRank() const
Definition pmesh.hpp:404
int GetNRanks() const
Definition pmesh.hpp:403
void Print(std::ostream &out=mfem::out, const std::string &comments="") const override
Definition pmesh.cpp:4800
Parallel non-linear operator on the true dofs.
Abstract class for PETSc's nonlinear solvers.
Definition petsc.hpp:907
void Mult(const Vector &b, Vector &x) const override
Application of the solver.
Definition petsc.cpp:4136
Wrapper for PETSc's matrix class.
Definition petsc.hpp:319
Abstract class for PETSc's preconditioners.
Definition petsc.hpp:805
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
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)
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
void Neg()
(*this) = -(*this)
Definition vector.cpp:375
void SetSubVector(const Array< int > &dofs, const real_t value)
Set the entries listed in dofs to the given value.
Definition vector.cpp:679
int Size() const
Returns the size of the vector.
Definition vector.hpp:226
real_t * GetData() const
Return a pointer to the beginning of the Vector data.
Definition vector.hpp:235
int open(const char hostname[], int port)
Open the socket stream on 'port' at 'hostname'.
int dim
Definition ex24.cpp:53
real_t mu
Definition ex25.cpp:140
int main()
HYPRE_Int HYPRE_BigInt
void add(const Vector &v1, const Vector &v2, Vector &v)
Definition vector.cpp:391
void MFEMInitializePetsc()
Convenience functions to initialize/finalize PETSc.
Definition petsc.cpp:206
void MFEMFinalizePetsc()
Definition petsc.cpp:246
float real_t
Definition config.hpp:43
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 visualize(ostream &os, ParMesh *mesh, ParGridFunction *deformed_nodes, ParGridFunction *field, const char *field_name=NULL, bool init_vis=false)
Definition ex10p.cpp:483
void InitialDeformation(const Vector &x, Vector &y)
Definition ex10p.cpp:766
void InitialVelocity(const Vector &x, Vector &v)
Definition ex10p.cpp:773
std::array< int, NCMesh::MaxFaceNodes > nodes