libMesh
fem_system_ex5.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 
18 
19 
20 // <h1>FEMSystem Example 5 - Unsteady Linear Elasticity with
21 // IsoGeometric Analysis</h1>
22 // \author Roy Stogner
23 // \date 2019
24 //
25 // This example shows how to solve the three-dimensional transient
26 // linear elasticity equations with elements suitable for IsoGeometric
27 // Analysis, as read from a DynaIO MeshInput.
28 //
29 // This is just FEMSystem Example 3 on an IGA mesh.
30 
31 // C++ includes
32 #include <iomanip>
33 #include <memory>
34 
35 // Basic include files
36 #include "libmesh/equation_systems.h"
37 #include "libmesh/error_vector.h"
38 #include "libmesh/getpot.h"
39 #include "libmesh/dyna_io.h"
40 #include "libmesh/exodusII_io.h"
41 #include "libmesh/kelly_error_estimator.h"
42 #include "libmesh/mesh.h"
43 #include "libmesh/mesh_communication.h"
44 #include "libmesh/mesh_generation.h"
45 #include "libmesh/enum_solver_package.h"
46 #include "libmesh/enum_solver_type.h"
47 #include "libmesh/numeric_vector.h"
48 
49 // The systems and solvers we may use
50 #include "elasticity_system.h"
51 #include "libmesh/diff_solver.h"
52 #include "libmesh/newmark_solver.h"
53 #include "libmesh/steady_solver.h"
54 #include "libmesh/euler_solver.h"
55 #include "libmesh/euler2_solver.h"
56 #include "libmesh/elem.h"
57 #include "libmesh/newton_solver.h"
58 #include "libmesh/eigen_sparse_linear_solver.h"
59 
60 #define x_scaling 1.3
61 
62 // Bring in everything from the libMesh namespace
63 using namespace libMesh;
64 
65 // The main program.
66 int main (int argc, char ** argv)
67 {
68  // Initialize libMesh.
69  LibMeshInit init (argc, argv);
70 
71  // This example requires a linear solver package.
72  libmesh_example_requires(libMesh::default_solver_package() != INVALID_SOLVER_PACKAGE,
73  "--enable-petsc, --enable-trilinos, or --enable-eigen");
74 
75  // This example requires 3D calculations
76  libmesh_example_requires(LIBMESH_DIM > 2, "3D support");
77 
78  // We use Dirichlet boundary conditions here
79 #ifndef LIBMESH_ENABLE_DIRICHLET
80  libmesh_example_requires(false, "--enable-dirichlet");
81 #endif
82 
83  // Parse the input file
84  GetPot infile("fem_system_ex5.in");
85 
86  // Override input file arguments from the command line
87  infile.parse_command_line(argc, argv);
88 
89  // Read in parameters from the input file
90  const Real deltat = infile("deltat", 0.25);
91  unsigned int n_timesteps = infile("n_timesteps", 1);
92 
93 #ifdef LIBMESH_HAVE_EXODUS_API
94  const unsigned int write_interval = infile("write_interval", 1);
95 #endif
96 
97  // Initialize the mesh
98  const unsigned int dim = infile("dim", 2);
99  libmesh_example_requires(dim <= LIBMESH_DIM, "2D/3D support");
100 
101  // Create a mesh distributed across the default MPI communicator.
102  Mesh mesh(init.comm(), dim);
103 
104  // Use an IsoGeometricAnalysis mesh?
105  const bool use_iga = infile("use_iga", true);
106 
107  // Declare a default FEType here to override if needed for IGA later
108  FEType fe_type;
109 
110  const std::string iga_filename = infile("iga_filename","DIE!");
111 
112  // Load an IGA pressurized cylinder mesh or build a cantilever mesh
113  if (use_iga)
114  {
115  DynaIO dyna_io(mesh);
116 
117  libMesh::out << "\nReading IGA mesh.\n" << std::endl;
118  if (mesh.processor_id() == 0)
119  dyna_io.read(iga_filename);
122 
123  fe_type.order = 2;
124  fe_type.family = RATIONAL_BERNSTEIN;
125  }
126  else
127  {
128  if (dim == 3)
130  (mesh, 32, 8, 4, 0., 1.*x_scaling, 0., 0.3, 0., 0.1, HEX8);
131  else if (dim == 2)
133  (mesh, 8, 4, 0., 1.*x_scaling, 0., 0.3, QUAD4);
134  else
135  libmesh_error_msg("Unsupported dim = " << dim);
136  }
137 
138  // Print information about the mesh to the screen.
139  mesh.print_info();
140 
141  // Change our boundary id definitions to match the problem mesh
142  if (dim == 2)
143  {
144  boundary_id_min_z = 99;
145  boundary_id_min_y = 0;
146  boundary_id_max_x = 1;
147  boundary_id_max_y = 2;
148  boundary_id_min_x = 3;
149  boundary_id_max_z = 99;
150  }
151 
152  // Let's add some node and edge boundary conditions.
153  // Each processor should know about each boundary condition it can
154  // see, so we loop over all elements, not just local elements.
155  for (const auto & elem : mesh.element_ptr_range())
156  {
157  if (!use_iga)
158  {
159  unsigned int side_max_x = 0, side_max_y = 0, side_min_z = 0;
160  bool
161  found_side_max_x = false, found_side_max_y = false,
162  found_side_min_z = false;
163  for (auto side : elem->side_index_range())
164  {
166  {
167  side_max_x = side;
168  found_side_max_x = true;
169  }
170 
172  {
173  side_max_y = side;
174  found_side_max_y = true;
175  }
176 
178  || dim == 2)
179  {
180  side_min_z = side;
181  found_side_min_z = true;
182  }
183  }
184 
185  // If elem has sides on boundaries
186  // BOUNDARY_ID_MAX_X, BOUNDARY_ID_MAX_Y, BOUNDARY_ID_MIN_Z
187  // then let's set a node boundary condition
188  if (found_side_max_x && found_side_max_y && found_side_min_z)
189  for (auto n : elem->node_index_range())
190  if (elem->is_node_on_side(n, side_max_x) &&
191  elem->is_node_on_side(n, side_max_y) &&
192  (dim == 2 || elem->is_node_on_side(n, side_min_z)))
193  mesh.get_boundary_info().add_node(elem->node_ptr(n), node_boundary_id);
194 
195  // If elem has sides on boundaries
196  // BOUNDARY_ID_MAX_X and BOUNDARY_ID_MIN_Z
197  // then let's set an edge boundary condition
198  if (found_side_max_x && found_side_min_z)
199  for (auto e : elem->edge_index_range())
200  if (elem->is_edge_on_side(e, side_max_x) &&
201  (dim == 2 || elem->is_edge_on_side(e, side_min_z)))
203  }
204  else // if (use_iga)
205  {
206  // On our IGA mesh, we don't have BCs set yet, but:
207  // Side 0 is the r=1 outer quarter circle
208  // Side 1 is the x=0 symmetry boundary
209  // Side 2 is the r=0.5 inner quarter circle
210  // Side 3 is the y=0 symmetry boundary
211 
212  // The bottom side of our IGA mesh file should have v
213  // displacement fixed
214  if (elem->type() == QUAD9 && !elem->neighbor_ptr(3))
216 
217  // The left/top side of our IGA mesh file should have u
218  // displacement fixed
219  if (elem->type() == QUAD9 && !elem->neighbor_ptr(1))
221 
222  // The inside of our cylinder should have pressure applied
223  if (elem->type() == QUAD9 && !elem->neighbor_ptr(2))
225  }
226  }
227 
228  // Create an equation systems object.
229  EquationSystems equation_systems (mesh);
230 
231  // Declare the system "Linear Elasticity" and its variables.
232  ElasticitySystem & system =
233  equation_systems.add_system<ElasticitySystem> ("Linear Elasticity");
234 
235  system.set_dim(dim);
236 
237  system.set_fe_type(fe_type);
238 
239  // Solve this as a time-dependent or steady system
240  std::string time_solver = infile("time_solver","DIE!");
241 
242  ExplicitSystem * v_system = nullptr;
243  ExplicitSystem * a_system = nullptr;
244 
245  if (time_solver == "newmark")
246  {
247  // Create ExplicitSystem to help output velocity
248  v_system = &equation_systems.add_system<ExplicitSystem> ("Velocity");
249  v_system->add_variable("u_vel", fe_type);
250  v_system->add_variable("v_vel", fe_type);
251  if (dim == 3)
252  v_system->add_variable("w_vel", fe_type);
253 
254  // Create ExplicitSystem to help output acceleration
255  a_system = &equation_systems.add_system<ExplicitSystem> ("Acceleration");
256  a_system->add_variable("u_accel", fe_type);
257  a_system->add_variable("v_accel", fe_type);
258  if (dim == 3)
259  a_system->add_variable("w_accel", fe_type);
260  }
261 
262  if (time_solver == "newmark")
263  system.time_solver = std::make_unique<NewmarkSolver>(system);
264 
265  else if (time_solver == "euler")
266  {
267  system.time_solver = std::make_unique<EulerSolver>(system);
268  EulerSolver & euler_solver = cast_ref<EulerSolver &>(*(system.time_solver.get()));
269  euler_solver.theta = infile("theta", 1.0);
270  }
271 
272  else if (time_solver == "euler2")
273  {
274  system.time_solver = std::make_unique<Euler2Solver>(system);
275  Euler2Solver & euler_solver = cast_ref<Euler2Solver &>(*(system.time_solver.get()));
276  euler_solver.theta = infile("theta", 1.0);
277  }
278 
279  else if (time_solver == "steady")
280  {
281  system.time_solver = std::make_unique<SteadySolver>(system);
282  libmesh_assert_equal_to (n_timesteps, 1);
283  }
284  else
285  libmesh_error_msg(std::string("ERROR: invalid time_solver ")+time_solver);
286 
287  // Initialize the system
288  equation_systems.init ();
289 
290  // Set the time stepping options
291  system.deltat = deltat;
292 
293  // And the nonlinear solver options
294  DiffSolver & solver = *(system.time_solver->diff_solver().get());
295  solver.quiet = infile("solver_quiet", true);
296  solver.verbose = !solver.quiet;
297  solver.max_nonlinear_iterations = infile("max_nonlinear_iterations", 15);
298  solver.relative_step_tolerance = infile("relative_step_tolerance", 1.e-3);
299  solver.relative_residual_tolerance = infile("relative_residual_tolerance", 0.0);
300  solver.absolute_residual_tolerance = infile("absolute_residual_tolerance", 0.0);
301 
302  // And the linear solver options
303  solver.max_linear_iterations = infile("max_linear_iterations", 50000);
304  solver.initial_linear_tolerance = infile("initial_linear_tolerance", 1.e-3);
305 
306  // And the system
307  system.print_element_jacobians = infile("print_element_jacobians", false);
308  system.print_element_solutions = infile("print_element_solutions", false);
309  system.print_element_residuals = infile("print_element_residuals", false);
310  system.print_residuals = infile("print_residuals", false);
311  system.print_jacobians = infile("print_residuals", false);
312  system.print_residual_norms = infile("print_residual_norms", false);
313  system.print_jacobian_norms = infile("print_residuals", false);
314 
315  // Print information about the system to the screen.
316  equation_systems.print_info();
317 
318  // If we're using EulerSolver or Euler2Solver, and we're using EigenSparseLinearSolver,
319  // then we need to reset the EigenSparseLinearSolver to use SPARSELU because BICGSTAB
320  // chokes on the Jacobian matrix we give it and Eigen's GMRES currently doesn't work.
321  NewtonSolver * newton_solver = dynamic_cast<NewtonSolver *>( &solver );
322  if (newton_solver &&
323  (time_solver == "euler" || time_solver == "euler2"))
324  {
325 #ifdef LIBMESH_HAVE_EIGEN_SPARSE
326  LinearSolver<Number> & linear_solver = newton_solver->get_linear_solver();
327  EigenSparseLinearSolver<Number> * eigen_linear_solver =
328  dynamic_cast<EigenSparseLinearSolver<Number> *>(&linear_solver);
329 
330  if (eigen_linear_solver )
331  eigen_linear_solver->set_solver_type(SPARSELU);
332 #endif
333  }
334 
335  if (time_solver == "newmark")
336  {
337  NewmarkSolver * newmark = cast_ptr<NewmarkSolver*>(system.time_solver.get());
338  newmark->compute_initial_accel();
339 
340  // Copy over initial velocity and acceleration for output.
341  // Note we can do this because of the matching variables/FE spaces
342  *(v_system->solution) = system.get_vector("_old_solution_rate");
343  *(a_system->solution) = system.get_vector("_old_solution_accel");
344  }
345 
346 #ifdef LIBMESH_HAVE_EXODUS_API
347  // Output initial state (if we can - IGA meshs don't serialize yet)
348  if (mesh.is_serial() || !use_iga)
349  {
350  std::ostringstream file_name;
351 
352  // We write the file in the ExodusII format.
353  file_name << std::string("out.")+time_solver+std::string(".e-s.")
354  << std::setw(3)
355  << std::setfill('0')
356  << std::right
357  << 0;
358 
359  ExodusII_IO(mesh).write_timestep(file_name.str(),
360  equation_systems,
361  1, // This number indicates how many time steps
362  // are being written to the file
363  system.time);
364  }
365 #endif // #ifdef LIBMESH_HAVE_EXODUS_API
366 
367  // Now we begin the timestep loop to compute the time-accurate
368  // solution of the equations.
369  for (unsigned int t_step=0; t_step != n_timesteps; ++t_step)
370  {
371  // A pretty update message
372  libMesh::out << "\n\nSolving time step "
373  << t_step
374  << ", time = "
375  << system.time
376  << std::endl;
377 
378  system.solve();
379 
380  // Advance to the next timestep in a transient problem
381  system.time_solver->advance_timestep();
382 
383  // Copy over updated velocity and acceleration for output.
384  // Note we can do this because of the matching variables/FE spaces
385  if (time_solver == "newmark")
386  {
387  *(v_system->solution) = system.get_vector("_old_solution_rate");
388  *(a_system->solution) = system.get_vector("_old_solution_accel");
389  }
390 
391 #ifdef LIBMESH_HAVE_EXODUS_API
392  // Write out this timestep if we're requested to
393  // (if we can - IGA meshs don't serialize yet)
394  if ((mesh.is_serial() || !use_iga) &&
395  ((t_step+1)%write_interval == 0))
396  {
397  std::ostringstream file_name;
398 
399  // We write the file in the ExodusII format.
400  file_name << std::string("out.")+time_solver+std::string(".e-s.")
401  << std::setw(3)
402  << std::setfill('0')
403  << std::right
404  << t_step+1;
405 
406  ExodusII_IO(mesh).write_timestep(file_name.str(),
407  equation_systems,
408  1, // This number indicates how many time steps
409  // are being written to the file
410  system.time);
411  }
412 #endif // #ifdef LIBMESH_HAVE_EXODUS_API
413  }
414 
415  // All done.
416  return 0;
417 }
class FEType hides (possibly multiple) FEFamily and approximation orders, thereby enabling specialize...
Definition: fe_type.h:196
FEFamily family
The type of finite element.
Definition: fe_type.h:221
Real time
For time-dependent problems, this is the time t at the beginning of the current timestep.
Definition: system.h:1615
boundary_id_type boundary_id_min_x
virtual void read(const std::string &name) override
Reads in a mesh in the Dyna format from the ASCII file given by name.
Definition: dyna_io.C:139
LinearSolver< Number > & get_linear_solver()
Definition: newton_solver.h:81
This is the EquationSystems class.
const boundary_id_type edge_boundary_id
Reading and writing meshes in (a subset of) LS-DYNA format.
Definition: dyna_io.h:52
bool has_boundary_id(const Node *const node, const boundary_id_type id) const
bool quiet
The DiffSolver should not print anything to libMesh::out unless quiet is set to false; default is tru...
Definition: diff_solver.h:160
bool print_jacobian_norms
Set print_jacobian_norms to true to print |J| whenever it is assembled.
Definition: diff_system.h:361
unsigned int dim
The ExodusII_IO class implements reading meshes in the ExodusII file format from Sandia National Labs...
Definition: exodusII_io.h:52
unsigned int max_nonlinear_iterations
The DiffSolver should exit in failure if max_nonlinear_iterations is exceeded and continue_after_max_...
Definition: diff_solver.h:154
void prepare_for_use(const bool skip_renumber_nodes_and_elements, const bool skip_find_neighbors)
Prepare a newly ecreated (or read) mesh for use.
Definition: mesh_base.C:759
Real absolute_residual_tolerance
The DiffSolver should exit after the residual is reduced to either less than absolute_residual_tolera...
Definition: diff_solver.h:189
std::unique_ptr< TimeSolver > time_solver
A pointer to the solver object we&#39;re going to use.
Definition: diff_system.h:253
void print_info(std::ostream &os=libMesh::out) const
Prints information about the equation systems, by default to libMesh::out.
MeshBase & mesh
This class provides an interface to Eigen iterative solvers that is compatible with the libMesh Linea...
This class defines a Newmark time integrator for second order (in time) DifferentiableSystems.
void set_dim(unsigned int dim)
OrderWrapper order
The approximation order of the element.
Definition: fe_type.h:215
void build_square(UnstructuredMesh &mesh, const unsigned int nx, const unsigned int ny, const Real xmin=0., const Real xmax=1., const Real ymin=0., const Real ymax=1., const ElemType type=INVALID_ELEM, const bool gauss_lobatto_grid=false)
A specialized build_cube() for 2D meshes.
The LibMeshInit class, when constructed, initializes the dependent libraries (e.g.
Definition: libmesh.h:90
The libMesh namespace provides an interface to certain functionality in the library.
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition: mesh_base.h:165
bool print_jacobians
Set print_jacobians to true to print J whenever it is assembled.
Definition: diff_system.h:366
boundary_id_type boundary_id_max_x
This class defines a solver which uses the default libMesh linear solver in a quasiNewton method to h...
Definition: newton_solver.h:46
virtual void compute_initial_accel()
This method uses the specified initial displacement and velocity to compute the initial acceleration ...
bool print_element_residuals
Set print_element_residuals to true to print each R_elem contribution.
Definition: diff_system.h:376
bool print_element_solutions
Set print_element_solutions to true to print each U_elem input.
Definition: diff_system.h:371
SolverPackage default_solver_package()
Definition: libmesh.C:1117
This is a generic class that defines a solver to handle ImplicitSystem classes, including NonlinearIm...
Definition: diff_solver.h:68
virtual bool is_serial() const
Definition: mesh_base.h:211
void add_node(const Node *node, const boundary_id_type id)
Add Node node with boundary id id to the boundary information data structures.
This class defines a theta-method (defaulting to Backward Euler with theta = 1.0) solver to handle ti...
Definition: euler2_solver.h:48
This is the MeshCommunication class.
bool print_residual_norms
Set print_residual_norms to true to print |F| whenever it is assembled.
Definition: diff_system.h:351
boundary_id_type boundary_id_max_z
const boundary_id_type node_boundary_id
Real deltat
For time-dependent problems, this is the amount delta t to advance the solution in time...
Definition: diff_system.h:280
unsigned int max_linear_iterations
Each linear solver step should exit after max_linear_iterations is exceeded.
Definition: diff_solver.h:146
void print_info(std::ostream &os=libMesh::out, const unsigned int verbosity=0, const bool global=true) const
Prints relevant information about the mesh.
Definition: mesh_base.C:1562
std::unique_ptr< NumericVector< Number > > solution
Data structure to hold solution values.
Definition: system.h:1593
bool print_residuals
Set print_residuals to true to print F whenever it is assembled.
Definition: diff_system.h:356
void init(triangulateio &t)
Initializes the fields of t to nullptr/0 as necessary.
void set_fe_type(const FEType &fe_type)
boundary_id_type boundary_id_max_y
unsigned int add_variable(std::string_view var, const FEType &type, const std::set< subdomain_id_type > *const active_subdomains=nullptr)
Adds the variable var to the list of variables for this system.
Definition: system.C:1357
void set_solver_type(const SolverType st)
Sets the type of solver to use.
const boundary_id_type pressure_boundary_id
This class defines a theta-method Euler (defaulting to Backward Euler with theta = 1...
Definition: euler_solver.h:43
bool verbose
The DiffSolver may print a lot more to libMesh::out if verbose is set to true; default is false...
Definition: diff_solver.h:166
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
void write_timestep(const std::string &fname, const EquationSystems &es, const int timestep, const Real time, const std::set< std::string > *system_names=nullptr)
Writes out the solution at a specific timestep.
Definition: exodusII_io.C:2017
OStreamProxy out
void broadcast(MeshBase &) const
Finds all the processors that may contain elements that neighbor my elements.
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
Add side side of element number elem with boundary id id to the boundary information data structure...
const boundary_id_type fixed_u_boundary_id
bool print_element_jacobians
Set print_element_jacobians to true to print each J_elem contribution.
Definition: diff_system.h:381
int main(int argc, char **argv)
virtual void solve() override
Invokes the solver associated with the system.
Definition: fem_system.C:1076
virtual void init()
Initialize all the systems.
virtual System & add_system(std::string_view system_type, std::string_view name)
Add the system of type system_type named name to the systems array.
The Mesh class is a thin wrapper, around the ReplicatedMesh class by default.
Definition: mesh.h:50
processor_id_type processor_id() const
boundary_id_type boundary_id_min_y
double initial_linear_tolerance
Any required linear solves will at first be done with this tolerance; the DiffSolver may tighten the ...
Definition: diff_solver.h:208
void build_cube(UnstructuredMesh &mesh, const unsigned int nx=0, const unsigned int ny=0, const unsigned int nz=0, const Real xmin=0., const Real xmax=1., const Real ymin=0., const Real ymax=1., const Real zmin=0., const Real zmax=1., const ElemType type=INVALID_ELEM, const bool gauss_lobatto_grid=false)
Builds a (elements) cube.
Manages consistently variables, degrees of freedom, and coefficient vectors for explicit systems...
Real relative_residual_tolerance
Definition: diff_solver.h:190
const NumericVector< Number > & get_vector(std::string_view vec_name) const
Definition: system.C:943
void add_edge(const dof_id_type elem, const unsigned short int edge, const boundary_id_type id)
Add edge edge of element number elem with boundary id id to the boundary information data structure...
boundary_id_type boundary_id_min_z
const boundary_id_type fixed_v_boundary_id