MFEM v4.8.0
Finite element discretization library
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
ex10.cpp
Go to the documentation of this file.
1// MFEM Example 10
2// SUNDIALS Modification
3//
4// Compile with:
5// make ex10 (GNU make)
6// make sundials_ex10 (CMake)
7//
8// Sample runs:
9// ex10 -m ../../data/beam-quad.mesh -r 2 -o 2 -s 12 -dt 0.15 -vs 10
10// ex10 -m ../../data/beam-tri.mesh -r 2 -o 2 -s 16 -dt 0.3 -vs 5
11// ex10 -m ../../data/beam-hex.mesh -r 1 -o 2 -s 12 -dt 0.2 -vs 5
12// ex10 -m ../../data/beam-tri.mesh -r 2 -o 2 -s 2 -dt 3 -nls 1
13// ex10 -m ../../data/beam-quad.mesh -r 2 -o 2 -s 2 -dt 3 -nls 2
14// ex10 -m ../../data/beam-hex.mesh -r 1 -o 2 -s 2 -dt 3 -nls 4
15// ex10 -m ../../data/beam-quad.mesh -r 2 -o 2 -s 14 -dt 0.15 -vs 10
16// ex10 -m ../../data/beam-tri.mesh -r 2 -o 2 -s 17 -dt 0.01 -vs 30
17// ex10 -m ../../data/beam-hex.mesh -r 1 -o 2 -s 14 -dt 0.15 -vs 10
18// ex10 -m ../../data/beam-quad-amr.mesh -r 2 -o 2 -s 12 -dt 0.15 -vs 10
19//
20// Description: This examples solves a time dependent nonlinear elasticity
21// problem of the form dv/dt = H(x) + S v, dx/dt = v, where H is a
22// hyperelastic model and S is a viscosity operator of Laplacian
23// type. The geometry of the domain is assumed to be as follows:
24//
25// +---------------------+
26// boundary --->| |
27// attribute 1 | |
28// (fixed) +---------------------+
29//
30// The example demonstrates the use of nonlinear operators (the
31// class HyperelasticOperator defining H(x)), as well as their
32// implicit time integration using a Newton method for solving an
33// associated reduced backward-Euler type nonlinear equation
34// (class ReducedSystemOperator). Each Newton step requires the
35// inversion of a Jacobian matrix, which is done through a
36// (preconditioned) inner solver. Note that implementing the
37// method HyperelasticOperator::ImplicitSolve is the only
38// requirement for high-order implicit (SDIRK) time integration.
39//
40// We recommend viewing examples 2 and 9 before viewing this
41// example.
42
43#include "mfem.hpp"
44#include <memory>
45#include <iostream>
46#include <fstream>
47#include <string>
48#include <map>
49
50#ifndef MFEM_USE_SUNDIALS
51#error This example requires that MFEM is built with MFEM_USE_SUNDIALS=YES
52#endif
53
54using namespace std;
55using namespace mfem;
56
57class ReducedSystemOperator;
58
59/** After spatial discretization, the hyperelastic model can be written as a
60 * system of ODEs:
61 * dv/dt = -M^{-1}*(H(x) + S*v)
62 * dx/dt = v,
63 * where x is the vector representing the deformation, v is the velocity field,
64 * M is the mass matrix, S is the viscosity matrix, and H(x) is the nonlinear
65 * hyperelastic operator.
66 *
67 * Class HyperelasticOperator represents the right-hand side of the above
68 * system of ODEs. */
69class HyperelasticOperator : public TimeDependentOperator
70{
71protected:
72 FiniteElementSpace &fespace;
73
74 BilinearForm M, S;
76 double viscosity;
77 HyperelasticModel *model;
78
79 CGSolver M_solver; // Krylov solver for inverting the mass matrix M
80 DSmoother M_prec; // Preconditioner for the mass matrix M
81
82 /** Nonlinear operator defining the reduced backward Euler equation for the
83 velocity. Used in the implementation of method ImplicitSolve. */
84 ReducedSystemOperator *reduced_oper;
85
86 /// Newton solver for the reduced backward Euler equation
87 NewtonSolver *newton_solver;
88
89 /// Solver for the Jacobian solve in the Newton method
90 Solver *J_solver;
91 /// Preconditioner for the Jacobian solve in the Newton method
92 Solver *J_prec;
93
94 mutable Vector z; // auxiliary vector
95
96 SparseMatrix *grad_H;
97 SparseMatrix *Jacobian;
98
99 double saved_gamma; // saved gamma value from implicit setup
100
101public:
102
103 HyperelasticOperator(FiniteElementSpace &f, Array<int> &ess_bdr,
104 double visc, double mu, double K,
105 int kinsol_nls_type = -1, double kinsol_damping = 0.0,
106 int kinsol_aa_n = 0);
107
108 /// Compute the right-hand side of the ODE system.
109 virtual void Mult(const Vector &vx, Vector &dvx_dt) const;
110
111 /** Solve the Backward-Euler equation: k = f(x + dt*k, t), for the unknown k.
112 This is the only requirement for high-order SDIRK implicit integration.*/
113 virtual void ImplicitSolve(const double dt, const Vector &x, Vector &k);
114
115
116 /// Custom Jacobian system solver for the SUNDIALS time integrators.
117 /** For the ODE system represented by HyperelasticOperator
118
119 M dv/dt = -(H(x) + S*v)
120 dx/dt = v,
121
122 this class facilitates the solution of linear systems of the form
123
124 (M + γS) yv + γJ yx = M bv, J=(dH/dx)(x)
125 - γ yv + yx = bx
126
127 for given bv, bx, x, and γ = GetTimeStep(). */
128
129 /** Linear solve applicable to the SUNDIALS format.
130 Solves (Mass - dt J) y = Mass b, where in our case:
131 Mass = | M 0 | J = | -S -grad_H | y = | v_hat | b = | b_v |
132 | 0 I | | I 0 | | x_hat | | b_x |
133 The result replaces the rhs b.
134 We substitute x_hat = b_x + dt v_hat and solve
135 (M + dt S + dt^2 grad_H) v_hat = M b_v - dt grad_H b_x. */
136
137 /** Setup the linear system. This method is used by the implicit
138 SUNDIALS solvers. */
139 virtual int SUNImplicitSetup(const Vector &y, const Vector &fy,
140 int jok, int *jcur, double gamma);
141
142 /** Solve the linear system. This method is used by the implicit
143 SUNDIALS solvers. */
144 virtual int SUNImplicitSolve(const Vector &b, Vector &x, double tol);
145
146 double ElasticEnergy(const Vector &x) const;
147 double KineticEnergy(const Vector &v) const;
148 void GetElasticEnergyDensity(const GridFunction &x, GridFunction &w) const;
149
150 virtual ~HyperelasticOperator();
151};
152
153/** Nonlinear operator of the form:
154 k --> (M + dt*S)*k + H(x + dt*v + dt^2*k) + S*v,
155 where M and S are given BilinearForms, H is a given NonlinearForm, v and x
156 are given vectors, and dt is a scalar. */
157class ReducedSystemOperator : public Operator
158{
159private:
160 BilinearForm *M, *S;
161 NonlinearForm *H;
162 mutable SparseMatrix *Jacobian;
163 double dt;
164 const Vector *v, *x;
165 mutable Vector w, z;
166
167public:
168 ReducedSystemOperator(BilinearForm *M_, BilinearForm *S_, NonlinearForm *H_);
169
170 /// Set current dt, v, x values - needed to compute action and Jacobian.
171 void SetParameters(double dt_, const Vector *v_, const Vector *x_);
172
173 /// Compute y = H(x + dt (v + dt k)) + M k + S (v + dt k).
174 virtual void Mult(const Vector &k, Vector &y) const;
175
176 /// Compute J = M + dt S + dt^2 grad_H(x + dt (v + dt k)).
177 virtual Operator &GetGradient(const Vector &k) const;
178
179 virtual ~ReducedSystemOperator();
180};
181
182
183/** Function representing the elastic energy density for the given hyperelastic
184 model+deformation. Used in HyperelasticOperator::GetElasticEnergyDensity. */
185class ElasticEnergyCoefficient : public Coefficient
186{
187private:
188 HyperelasticModel &model;
189 const GridFunction &x;
190 DenseMatrix J;
191
192public:
193 ElasticEnergyCoefficient(HyperelasticModel &m, const GridFunction &x_)
194 : model(m), x(x_) { }
195 virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip);
196 virtual ~ElasticEnergyCoefficient() { }
197};
198
199void InitialDeformation(const Vector &x, Vector &y);
200
201void InitialVelocity(const Vector &x, Vector &v);
202
203void visualize(ostream &os, Mesh *mesh, GridFunction *deformed_nodes,
204 GridFunction *field, const char *field_name = NULL,
205 bool init_vis = false);
206
207
208int main(int argc, char *argv[])
209{
210 // 0. Initialize SUNDIALS.
212
213 // 1. Parse command-line options.
214 const char *mesh_file = "../../data/beam-quad.mesh";
215 int ref_levels = 2;
216 int order = 2;
217 int ode_solver_type = 3;
218 double t_final = 300.0;
219 double dt = 3.0;
220 double visc = 1e-2;
221 double mu = 0.25;
222 double K = 5.0;
223 bool visualization = true;
224 int nonlinear_solver_type = 0;
225 int vis_steps = 1;
226 double kinsol_damping = 0.0;
227 int kinsol_aa_n = -1;
228
229 // Relative and absolute tolerances for CVODE and ARKODE.
230 const double reltol = 1e-1, abstol = 1e-1;
231 // Since this example uses the loose tolerances defined above, it is
232 // necessary to lower the linear solver tolerance for CVODE which is relative
233 // to the above tolerances.
234 const double cvode_eps_lin = 1e-4;
235 // Similarly, the nonlinear tolerance for ARKODE needs to be tightened.
236 const double arkode_eps_nonlin = 1e-6;
237
238 OptionsParser args(argc, argv);
239 args.AddOption(&mesh_file, "-m", "--mesh",
240 "Mesh file to use.");
241 args.AddOption(&ref_levels, "-r", "--refine",
242 "Number of times to refine the mesh uniformly.");
243 args.AddOption(&order, "-o", "--order",
244 "Order (degree) of the finite elements.");
245 args.AddOption(&ode_solver_type, "-s", "--ode-solver",
246 "ODE solver:\n\t"
247 "1 - Backward Euler,\n\t"
248 "2 - SDIRK2, L-stable\n\t"
249 "3 - SDIRK3, L-stable\n\t"
250 "4 - Implicit Midpoint,\n\t"
251 "5 - SDIRK2, A-stable,\n\t"
252 "6 - SDIRK3, A-stable,\n\t"
253 "7 - Forward Euler,\n\t"
254 "8 - RK2,\n\t"
255 "9 - RK3 SSP,\n\t"
256 "10 - RK4,\n\t"
257 "11 - CVODE implicit BDF, approximate Jacobian,\n\t"
258 "12 - CVODE implicit BDF, specified Jacobian,\n\t"
259 "13 - CVODE implicit ADAMS, approximate Jacobian,\n\t"
260 "14 - CVODE implicit ADAMS, specified Jacobian,\n\t"
261 "15 - ARKODE implicit, approximate Jacobian,\n\t"
262 "16 - ARKODE implicit, specified Jacobian,\n\t"
263 "17 - ARKODE explicit, 4th order.");
264 args.AddOption(&nonlinear_solver_type, "-nls", "--nonlinear-solver",
265 "Nonlinear system solver:\n\t"
266 "0 - MFEM Newton method,\n\t"
267 "1 - KINSOL Newton method,\n\t"
268 "2 - KINSOL Newton method with globalization,\n\t"
269 "3 - KINSOL fixed-point method (with or without AA),\n\t"
270 "4 - KINSOL Picard method (with or without AA).");
271 args.AddOption(&kinsol_damping, "-damp", "--kinsol-damping",
272 "Picard or Fixed-Point damping parameter (only valid with KINSOL): "
273 "0 < d <= 1.0");
274 args.AddOption(&kinsol_aa_n, "-aan", "--anderson-subspace",
275 "Anderson Acceleration subspace size (only valid with KINSOL)");
276 args.AddOption(&t_final, "-tf", "--t-final",
277 "Final time; start time is 0.");
278 args.AddOption(&dt, "-dt", "--time-step",
279 "Time step.");
280 args.AddOption(&visc, "-v", "--viscosity",
281 "Viscosity coefficient.");
282 args.AddOption(&mu, "-mu", "--shear-modulus",
283 "Shear modulus in the Neo-Hookean hyperelastic model.");
284 args.AddOption(&K, "-K", "--bulk-modulus",
285 "Bulk modulus in the Neo-Hookean hyperelastic model.");
286 args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
287 "--no-visualization",
288 "Enable or disable GLVis visualization.");
289 args.AddOption(&vis_steps, "-vs", "--visualization-steps",
290 "Visualize every n-th timestep.");
291 args.Parse();
292 if (!args.Good())
293 {
294 args.PrintUsage(cout);
295 return 1;
296 }
297 args.PrintOptions(cout);
298
299 // check for valid ODE solver option
300 if (ode_solver_type < 1 || ode_solver_type > 17)
301 {
302 cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
303 return 1;
304 }
305
306 // check for valid nonlinear solver options
307 if (nonlinear_solver_type < 0 || nonlinear_solver_type > 4)
308 {
309 cout << "Unknown nonlinear solver type: " << nonlinear_solver_type << "\n";
310 return 1;
311 }
312 if (kinsol_damping > 0.0 &&
313 !(nonlinear_solver_type == 3 || nonlinear_solver_type == 4))
314 {
315 cout << "Only KINSOL fixed-point and Picard methods can use damping\n";
316 return 1;
317 }
318 if (kinsol_aa_n > 0 &&
319 !(nonlinear_solver_type == 3 || nonlinear_solver_type == 4))
320 {
321 cout << "Only KINSOL fixed-point and Picard methods can use AA\n";
322 return 1;
323 }
324
325
326 // 2. Read the mesh from the given mesh file. We can handle triangular,
327 // quadrilateral, tetrahedral and hexahedral meshes with the same code.
328 Mesh *mesh = new Mesh(mesh_file, 1, 1);
329 int dim = mesh->Dimension();
330
331 // 3. Refine the mesh to increase the resolution. In this example we do
332 // 'ref_levels' of uniform refinement, where 'ref_levels' is a
333 // command-line parameter.
334 for (int lev = 0; lev < ref_levels; lev++)
335 {
336 mesh->UniformRefinement();
337 }
338
339 // 4. Define the vector finite element spaces representing the mesh
340 // deformation x, the velocity v, and the initial configuration, x_ref.
341 // Define also the elastic energy density, w, which is in a discontinuous
342 // higher-order space. Since x and v are integrated in time as a system,
343 // we group them together in block vector vx, with offsets given by the
344 // fe_offset array.
345 H1_FECollection fe_coll(order, dim);
346 FiniteElementSpace fespace(mesh, &fe_coll, dim);
347
348 int fe_size = fespace.GetTrueVSize();
349 cout << "Number of velocity/deformation unknowns: " << fe_size << endl;
350 Array<int> fe_offset(3);
351 fe_offset[0] = 0;
352 fe_offset[1] = fe_size;
353 fe_offset[2] = 2*fe_size;
354
355 BlockVector vx(fe_offset);
356 GridFunction v, x;
357 v.MakeTRef(&fespace, vx.GetBlock(0), 0);
358 x.MakeTRef(&fespace, vx.GetBlock(1), 0);
359
360 GridFunction x_ref(&fespace);
361 mesh->GetNodes(x_ref);
362
363 L2_FECollection w_fec(order + 1, dim);
364 FiniteElementSpace w_fespace(mesh, &w_fec);
365 GridFunction w(&w_fespace);
366
367 // 5. Set the initial conditions for v and x, and the boundary conditions on
368 // a beam-like mesh (see description above).
370 v.ProjectCoefficient(velo);
371 v.SetTrueVector();
373 x.ProjectCoefficient(deform);
374 x.SetTrueVector();
375
376 Array<int> ess_bdr(fespace.GetMesh()->bdr_attributes.Max());
377 ess_bdr = 0;
378 ess_bdr[0] = 1; // boundary attribute 1 (index 0) is fixed
379
380 // 6. Initialize the hyperelastic operator, the GLVis visualization and print
381 // the initial energies.
382 std::unique_ptr<HyperelasticOperator> oper;
383 if (nonlinear_solver_type == 0)
384 oper = std::make_unique<HyperelasticOperator>(fespace, ess_bdr, visc, mu,
385 K);
386 else
387 {
388 switch (nonlinear_solver_type)
389 {
390 case 1:
391 oper = std::make_unique<HyperelasticOperator>(fespace, ess_bdr,
392 visc, mu, K, KIN_NONE);
393 break;
394 case 2:
395 oper = std::make_unique<HyperelasticOperator>(fespace, ess_bdr,
396 visc, mu, K, KIN_LINESEARCH);
397 break;
398 case 3:
399 oper = std::make_unique<HyperelasticOperator>(fespace, ess_bdr,
400 visc, mu, K, KIN_FP, kinsol_damping, kinsol_aa_n);
401 break;
402 case 4:
403 oper = std::make_unique<HyperelasticOperator>(fespace, ess_bdr,
404 visc, mu, K, KIN_PICARD, kinsol_damping, kinsol_aa_n);
405 break;
406 }
407 }
408
409 socketstream vis_v, vis_w;
410 if (visualization)
411 {
412 char vishost[] = "localhost";
413 int visport = 19916;
414 vis_v.open(vishost, visport);
415 vis_v.precision(8);
417 visualize(vis_v, mesh, &x, &v, "Velocity", true);
418 vis_w.open(vishost, visport);
419 if (vis_w)
420 {
421 oper->GetElasticEnergyDensity(x, w);
422 vis_w.precision(8);
423 visualize(vis_w, mesh, &x, &w, "Elastic energy density", true);
424 }
425 }
426
427 double ee0 = oper->ElasticEnergy(x.GetTrueVector());
428 double ke0 = oper->KineticEnergy(v.GetTrueVector());
429 cout << "initial elastic energy (EE) = " << ee0 << endl;
430 cout << "initial kinetic energy (KE) = " << ke0 << endl;
431 cout << "initial total energy (TE) = " << (ee0 + ke0) << endl;
432
433 // 7. Define the ODE solver used for time integration. Several implicit
434 // singly diagonal implicit Runge-Kutta (SDIRK) methods, as well as
435 // explicit Runge-Kutta methods are available.
436 double t = 0.0;
437 oper->SetTime(t);
438
439 ODESolver *ode_solver = NULL;
440 CVODESolver *cvode = NULL;
441 ARKStepSolver *arkode = NULL;
442 switch (ode_solver_type)
443 {
444 // Implicit L-stable methods
445 case 1: ode_solver = new BackwardEulerSolver; break;
446 case 2: ode_solver = new SDIRK23Solver(2); break;
447 case 3: ode_solver = new SDIRK33Solver; break;
448 // Implicit A-stable methods (not L-stable)
449 case 4: ode_solver = new ImplicitMidpointSolver; break;
450 case 5: ode_solver = new SDIRK23Solver; break;
451 case 6: ode_solver = new SDIRK34Solver; break;
452 // Explicit methods
453 case 7: ode_solver = new ForwardEulerSolver; break;
454 case 8: ode_solver = new RK2Solver(0.5); break; // midpoint method
455 case 9: ode_solver = new RK3SSPSolver; break;
456 case 10: ode_solver = new RK4Solver; break;
457 // CVODE BDF
458 case 11:
459 case 12:
460 cvode = new CVODESolver(CV_BDF);
461 cvode->Init(*oper);
462 cvode->SetSStolerances(reltol, abstol);
463 CVodeSetEpsLin(cvode->GetMem(), cvode_eps_lin);
464 cvode->SetMaxStep(dt);
465 if (ode_solver_type == 11)
466 {
468 }
469 ode_solver = cvode; break;
470 // CVODE Adams
471 case 13:
472 case 14:
473 cvode = new CVODESolver(CV_ADAMS);
474 cvode->Init(*oper);
475 cvode->SetSStolerances(reltol, abstol);
476 CVodeSetEpsLin(cvode->GetMem(), cvode_eps_lin);
477 cvode->SetMaxStep(dt);
478 if (ode_solver_type == 13)
479 {
481 }
482 ode_solver = cvode; break;
483 // ARKStep Implicit methods
484 case 15:
485 case 16:
487 arkode->Init(*oper);
488 arkode->SetSStolerances(reltol, abstol);
489#if MFEM_SUNDIALS_VERSION < 70100
490 ARKStepSetNonlinConvCoef(arkode->GetMem(), arkode_eps_nonlin);
491#else
492 ARKodeSetNonlinConvCoef(arkode->GetMem(), arkode_eps_nonlin);
493#endif
494 arkode->SetMaxStep(dt);
495 if (ode_solver_type == 15)
496 {
497 arkode->UseSundialsLinearSolver();
498 }
499 ode_solver = arkode; break;
500 // ARKStep Explicit methods
501 case 17:
503 arkode->Init(*oper);
504 arkode->SetSStolerances(reltol, abstol);
505 arkode->SetMaxStep(dt);
506 ode_solver = arkode; break;
507 }
508
509 // Initialize MFEM integrators, SUNDIALS integrators are initialized above
510 if (ode_solver_type < 11) { ode_solver->Init(*oper); }
511
512 // 8. Perform time-integration (looping over the time iterations, ti, with a
513 // time-step dt).
514 bool last_step = false;
515 for (int ti = 1; !last_step; ti++)
516 {
517 double dt_real = min(dt, t_final - t);
518
519 ode_solver->Step(vx, t, dt_real);
520
521 last_step = (t >= t_final - 1e-8*dt);
522
523 if (last_step || (ti % vis_steps) == 0)
524 {
525 double ee = oper->ElasticEnergy(x.GetTrueVector());
526 double ke = oper->KineticEnergy(v.GetTrueVector());
527
528 cout << "step " << ti << ", t = " << t << ", EE = " << ee << ", KE = "
529 << ke << ", ΔTE = " << (ee+ke)-(ee0+ke0) << endl;
530
531 if (cvode) { cvode->PrintInfo(); }
532 else if (arkode) { arkode->PrintInfo(); }
533
534 if (visualization)
535 {
537 visualize(vis_v, mesh, &x, &v);
538 if (vis_w)
539 {
540 oper->GetElasticEnergyDensity(x, w);
541 visualize(vis_w, mesh, &x, &w);
542 }
543 }
544 }
545 }
546
547 // 9. Save the displaced mesh, the velocity and elastic energy.
548 {
550 GridFunction *nodes = &x;
551 int owns_nodes = 0;
552 mesh->SwapNodes(nodes, owns_nodes);
553 ofstream mesh_ofs("deformed.mesh");
554 mesh_ofs.precision(8);
555 mesh->Print(mesh_ofs);
556 mesh->SwapNodes(nodes, owns_nodes);
557 ofstream velo_ofs("velocity.sol");
558 velo_ofs.precision(8);
559 v.Save(velo_ofs);
560 ofstream ee_ofs("elastic_energy.sol");
561 ee_ofs.precision(8);
562 oper->GetElasticEnergyDensity(x, w);
563 w.Save(ee_ofs);
564 }
565
566 // 10. Free the used memory.
567 delete ode_solver;
568 delete mesh;
569
570 return 0;
571}
572
573
574void visualize(ostream &os, Mesh *mesh, GridFunction *deformed_nodes,
575 GridFunction *field, const char *field_name, bool init_vis)
576{
577 if (!os)
578 {
579 return;
580 }
581
582 GridFunction *nodes = deformed_nodes;
583 int owns_nodes = 0;
584
585 mesh->SwapNodes(nodes, owns_nodes);
586
587 os << "solution\n" << *mesh << *field;
588
589 mesh->SwapNodes(nodes, owns_nodes);
590
591 if (init_vis)
592 {
593 os << "window_size 800 800\n";
594 os << "window_title '" << field_name << "'\n";
595 if (mesh->SpaceDimension() == 2)
596 {
597 os << "view 0 0\n"; // view from top
598 os << "keys jl\n"; // turn off perspective and light
599 }
600 os << "keys cm\n"; // show colorbar and mesh
601 os << "autoscale value\n"; // update value-range; keep mesh-extents fixed
602 os << "pause\n";
603 }
604 os << flush;
605}
606
607
608ReducedSystemOperator::ReducedSystemOperator(
610 : Operator(M_->Height()), M(M_), S(S_), H(H_), Jacobian(NULL),
611 dt(0.0), v(NULL), x(NULL), w(height), z(height)
612{ }
613
614void ReducedSystemOperator::SetParameters(double dt_, const Vector *v_,
615 const Vector *x_)
616{
617 dt = dt_; v = v_; x = x_;
618}
619
620void ReducedSystemOperator::Mult(const Vector &k, Vector &y) const
621{
622 // compute: y = H(x + dt*(v + dt*k)) + M*k + S*(v + dt*k)
623 add(*v, dt, k, w);
624 add(*x, dt, w, z);
625 H->Mult(z, y);
626 M->AddMult(k, y);
627 S->AddMult(w, y);
628}
629
630Operator &ReducedSystemOperator::GetGradient(const Vector &k) const
631{
632 delete Jacobian;
633 Jacobian = Add(1.0, M->SpMat(), dt, S->SpMat());
634 add(*v, dt, k, w);
635 add(*x, dt, w, z);
636 SparseMatrix *grad_H = dynamic_cast<SparseMatrix *>(&H->GetGradient(z));
637 Jacobian->Add(dt*dt, *grad_H);
638 return *Jacobian;
639}
640
641ReducedSystemOperator::~ReducedSystemOperator()
642{
643 delete Jacobian;
644}
645
646
647HyperelasticOperator::HyperelasticOperator(FiniteElementSpace &f,
648 Array<int> &ess_bdr, double visc,
649 double mu, double K,
650 int kinsol_nls_type,
651 double kinsol_damping,
652 int kinsol_aa_n)
653 : TimeDependentOperator(2*f.GetTrueVSize(), 0.0), fespace(f),
654 M(&fespace), S(&fespace), H(&fespace),
655 viscosity(visc), z(height/2),
656 grad_H(NULL), Jacobian(NULL)
657{
658 const double rel_tol = 1e-8;
659 const int skip_zero_entries = 0;
660
661 const double ref_density = 1.0; // density in the reference configuration
662 ConstantCoefficient rho0(ref_density);
663 M.AddDomainIntegrator(new VectorMassIntegrator(rho0));
664 M.Assemble(skip_zero_entries);
665 Array<int> ess_tdof_list;
666 fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
667 SparseMatrix tmp;
668 M.FormSystemMatrix(ess_tdof_list, tmp);
669
670 M_solver.iterative_mode = false;
671 M_solver.SetRelTol(rel_tol);
672 M_solver.SetAbsTol(0.0);
673 M_solver.SetMaxIter(30);
674 M_solver.SetPrintLevel(0);
675 M_solver.SetPreconditioner(M_prec);
676 M_solver.SetOperator(M.SpMat());
677
678 model = new NeoHookeanModel(mu, K);
679 H.AddDomainIntegrator(new HyperelasticNLFIntegrator(model));
680 H.SetEssentialTrueDofs(ess_tdof_list);
681
682 ConstantCoefficient visc_coeff(viscosity);
683 S.AddDomainIntegrator(new VectorDiffusionIntegrator(visc_coeff));
684 S.Assemble(skip_zero_entries);
685 S.FormSystemMatrix(ess_tdof_list, tmp);
686
687 reduced_oper = new ReducedSystemOperator(&M, &S, &H);
688
689#ifndef MFEM_USE_SUITESPARSE
690 J_prec = new DSmoother(1);
691 MINRESSolver *J_minres = new MINRESSolver;
692 J_minres->SetRelTol(rel_tol);
693 J_minres->SetAbsTol(0.0);
694 J_minres->SetMaxIter(300);
695 J_minres->SetPrintLevel(-1);
696 J_minres->SetPreconditioner(*J_prec);
697 J_solver = J_minres;
698#else
699 J_solver = new UMFPackSolver;
700 J_prec = NULL;
701#endif
702
703 if (kinsol_nls_type > 0)
704 {
705 KINSolver *kinsolver = new KINSolver(kinsol_nls_type, true);
706 if (kinsol_nls_type != KIN_PICARD)
707 {
708 kinsolver->SetJFNK(true);
709 kinsolver->SetLSMaxIter(100);
710 }
711 if (kinsol_aa_n > 0)
712 {
713 kinsolver->EnableAndersonAcc(kinsol_aa_n);
714 }
715 newton_solver = kinsolver;
716 newton_solver->SetOperator(*reduced_oper);
717 newton_solver->SetMaxIter(200);
718 newton_solver->SetRelTol(rel_tol);
719 newton_solver->SetPrintLevel(0);
720 kinsolver->SetMaxSetupCalls(4);
721 if (kinsol_damping > 0.0)
722 {
723 kinsolver->SetDamping(kinsol_damping);
724 }
725 }
726 else
727 {
728 newton_solver = new NewtonSolver();
729 newton_solver->SetOperator(*reduced_oper);
730 newton_solver->SetMaxIter(10);
731 newton_solver->SetRelTol(rel_tol);
732 newton_solver->SetPrintLevel(-1);
733 }
734 newton_solver->SetSolver(*J_solver);
735 newton_solver->iterative_mode = false;
736}
737
738void HyperelasticOperator::Mult(const Vector &vx, Vector &dvx_dt) const
739{
740 // Create views to the sub-vectors v, x of vx, and dv_dt, dx_dt of dvx_dt
741 int sc = height/2;
742 Vector v(vx.GetData() + 0, sc);
743 Vector x(vx.GetData() + sc, sc);
744 Vector dv_dt(dvx_dt.GetData() + 0, sc);
745 Vector dx_dt(dvx_dt.GetData() + sc, sc);
746
747 H.Mult(x, z);
748 if (viscosity != 0.0)
749 {
750 S.AddMult(v, z);
751 }
752 z.Neg(); // z = -z
753 M_solver.Mult(z, dv_dt);
754
755 dx_dt = v;
756}
757
758void HyperelasticOperator::ImplicitSolve(const double dt,
759 const Vector &vx, Vector &dvx_dt)
760{
761 int sc = height/2;
762 Vector v(vx.GetData() + 0, sc);
763 Vector x(vx.GetData() + sc, sc);
764 Vector dv_dt(dvx_dt.GetData() + 0, sc);
765 Vector dx_dt(dvx_dt.GetData() + sc, sc);
766
767 // By eliminating kx from the coupled system:
768 // kv = -M^{-1}*[H(x + dt*kx) + S*(v + dt*kv)]
769 // kx = v + dt*kv
770 // we reduce it to a nonlinear equation for kv, represented by the
771 // reduced_oper. This equation is solved with the newton_solver
772 // object (using J_solver and J_prec internally).
773 reduced_oper->SetParameters(dt, &v, &x);
774 Vector zero; // empty vector is interpreted as zero r.h.s. by NewtonSolver
775 newton_solver->Mult(zero, dv_dt);
776 MFEM_VERIFY(newton_solver->GetConverged(),
777 "Nonlinear solver did not converge.");
778#ifdef MFEM_DEBUG
779 cout << " num nonlin sol iters = " << newton_solver->GetNumIterations()
780 << ", final norm = " << newton_solver->GetFinalNorm() << '\n';
781#endif
782 add(v, dt, dv_dt, dx_dt);
783}
784
785int HyperelasticOperator::SUNImplicitSetup(const Vector &y,
786 const Vector &fy, int jok, int *jcur,
787 double gamma)
788{
789 int sc = y.Size() / 2;
790 const Vector x(y.GetData() + sc, sc);
791
792 // J = M + dt*(S + dt*grad(H))
793 if (Jacobian) { delete Jacobian; }
794 Jacobian = Add(1.0, M.SpMat(), gamma, S.SpMat());
795 grad_H = dynamic_cast<SparseMatrix *>(&H.GetGradient(x));
796 Jacobian->Add(gamma * gamma, *grad_H);
797
798 // Set Jacobian solve operator
799 J_solver->SetOperator(*Jacobian);
800
801 // Indicate that the Jacobian was updated
802 *jcur = 1;
803
804 // Save gamma for use in solve
805 saved_gamma = gamma;
806
807 // Return success
808 return 0;
809}
810
811int HyperelasticOperator::SUNImplicitSolve(const Vector &b, Vector &x,
812 double tol)
813{
814 int sc = b.Size() / 2;
815 Vector b_v(b.GetData() + 0, sc);
816 Vector b_x(b.GetData() + sc, sc);
817 Vector x_v(x.GetData() + 0, sc);
818 Vector x_x(x.GetData() + sc, sc);
819 Vector rhs(sc);
820
821 // rhs = M b_v - dt*grad(H) b_x
822 grad_H->Mult(b_x, rhs);
823 rhs *= -saved_gamma;
824 M.AddMult(b_v, rhs);
825
826 J_solver->iterative_mode = false;
827 J_solver->Mult(rhs, x_v);
828
829 add(b_x, saved_gamma, x_v, x_x);
830
831 return 0;
832}
833
834double HyperelasticOperator::ElasticEnergy(const Vector &x) const
835{
836 return H.GetEnergy(x);
837}
838
839double HyperelasticOperator::KineticEnergy(const Vector &v) const
840{
841 return 0.5*M.InnerProduct(v, v);
842}
843
844void HyperelasticOperator::GetElasticEnergyDensity(
845 const GridFunction &x, GridFunction &w) const
846{
847 ElasticEnergyCoefficient w_coeff(*model, x);
848 w.ProjectCoefficient(w_coeff);
849}
850
851HyperelasticOperator::~HyperelasticOperator()
852{
853 delete Jacobian;
854 delete newton_solver;
855 delete J_solver;
856 delete J_prec;
857 delete reduced_oper;
858 delete model;
859}
860
861
862double ElasticEnergyCoefficient::Eval(ElementTransformation &T,
863 const IntegrationPoint &ip)
864{
865 model.SetTransformation(T);
866 x.GetVectorGradient(T, J);
867 // return model.EvalW(J); // in reference configuration
868 return model.EvalW(J)/J.Det(); // in deformed configuration
869}
870
871
873{
874 // set the initial configuration to be the same as the reference, stress
875 // free, configuration
876 y = x;
877}
878
879void InitialVelocity(const Vector &x, Vector &v)
880{
881 const int dim = x.Size();
882 const double s = 0.1/64.;
883
884 v = 0.0;
885 v(dim-1) = s*x(0)*x(0)*(8.0-x(0));
886 v(0) = -s*x(0)*x(0);
887}
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.
@ IMPLICIT
Implicit RK method.
Definition sundials.hpp:717
@ EXPLICIT
Explicit RK method.
Definition sundials.hpp:716
void UseSundialsLinearSolver()
Attach a SUNDIALS GMRES linear solver to ARKode.
void Init(TimeDependentOperator &f_) override
Initialize ARKode: calls ARKStepCreate() to create the ARKStep memory and set some defaults.
void SetSStolerances(double reltol, double abstol)
Set the scalar relative and scalar absolute tolerances.
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
A "square matrix" operator for the associated FE space and BLFIntegrators The sum of all the BLFInteg...
void AddMult(const Vector &x, Vector &y, const real_t a=1.0) const override
Add the matrix vector multiple to a vector: .
real_t InnerProduct(const Vector &x, const Vector &y) const
Compute .
const SparseMatrix & SpMat() const
Returns a const reference to the sparse matrix: .
A class to handle Vectors in a block fashion.
Vector & GetBlock(int i)
Get the i-th vector in the block.
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
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 for scaled Jacobi-type smoother of sparse matrix.
Data type dense matrix using column-major storage.
Definition densemat.hpp:24
real_t Det() const
Definition densemat.cpp:516
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition fespace.hpp:244
virtual int GetTrueVSize() const
Return the number of vector true (conforming) dofs.
Definition fespace.hpp:851
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
virtual void Save(std::ostream &out) const
Save the GridFunction to an output stream.
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
const Vector & GetTrueVector() const
Read only access to the (optional) internal true-dof Vector.
Definition gridfunc.hpp:133
virtual void ProjectCoefficient(Coefficient &coeff)
Project coeff Coefficient to this GridFunction. The projection computation depends on the choice of t...
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_)
Implicit midpoint method. A-stable, not L-stable.
Definition ode.hpp:350
Class for integration point with weight.
Definition intrules.hpp:35
real_t GetFinalNorm() const
Returns the final residual norm after termination of the solver during the last call to Mult().
Definition solvers.hpp:295
void SetRelTol(real_t rtol)
Definition solvers.hpp:229
int GetNumIterations() const
Returns the number of iterations taken during the last call to Mult()
Definition solvers.hpp:280
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
Interface to the KINSOL library – nonlinear solver methods.
Definition sundials.hpp:887
void SetJFNK(bool use_jfnk)
Set the Jacobian Free Newton Krylov flag. The default is false.
void SetOperator(const Operator &op) override
Set the nonlinear Operator of the system and initialize KINSOL.
void SetMaxSetupCalls(int max_calls)
Set maximum number of nonlinear iterations without a Jacobian update.
void SetDamping(double damping)
void EnableAndersonAcc(int n, int orth=KIN_ORTH_MGS, int delay=0, double damping=1.0)
Enable Anderson Acceleration for KIN_FP or KIN_PICARD.
void SetLSMaxIter(int m)
Set the maximum number of linear solver iterations.
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
virtual void Print(std::ostream &os=mfem::out, const std::string &comments="") const
Definition mesh.hpp:2433
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
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
Operator & GetGradient(const Vector &x) const override
Compute the gradient Operator of the NonlinearForm corresponding to the state x.
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].
Abstract operator.
Definition operator.hpp:25
int height
Dimension of the output / number of rows in the matrix.
Definition operator.hpp:27
virtual void Mult(const Vector &x, Vector &y) const =0
Operator application: y=A(x).
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.
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
virtual void SetOperator(const Operator &op)=0
Set/update the solver for the given operator.
Data type sparse matrix.
Definition sparsemat.hpp:51
void Add(const int i, const int j, const real_t val)
void Mult(const Vector &x, Vector &y) const override
Matrix vector multiplication.
void * GetMem() const
Access the SUNDIALS memory structure.
Definition sundials.hpp:407
static void Init()
Definition sundials.cpp:174
Base abstract class for first order time dependent operators.
Definition operator.hpp:332
Direct sparse solver using UMFPACK.
Definition solvers.hpp:1121
A general vector function coefficient.
Vector data type.
Definition vector.hpp:82
void Neg()
(*this) = -(*this)
Definition vector.cpp:375
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()
real_t b
Definition lissajous.cpp:42
void add(const Vector &v1, const Vector &v2, Vector &v)
Definition vector.cpp:391
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[]
std::array< int, NCMesh::MaxFaceNodes > nodes
void visualize(ostream &os, Mesh *mesh, GridFunction *deformed_nodes, GridFunction *field, const char *field_name=NULL, bool init_vis=false)
Definition ex10.cpp:574
void InitialDeformation(const Vector &x, Vector &y)
Definition ex10.cpp:872
void InitialVelocity(const Vector &x, Vector &v)
Definition ex10.cpp:879