libMesh
Loading...
Searching...
No Matches
fem_system_ex5.C
Go to the documentation of this file.
1// The libMesh Finite Element Library.
2// Copyright (C) 2002-2026 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
63using namespace libMesh;
64
65// The main program.
66int 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.
140
141 // Change our boundary id definitions to match the problem mesh
142 if (dim == 2)
143 {
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 // We're done modifying the BoundaryInfo; get its caches up to date.
230
231 // Create an equation systems object.
232 EquationSystems equation_systems (mesh);
233
234 // Declare the system "Linear Elasticity" and its variables.
235 ElasticitySystem & system =
236 equation_systems.add_system<ElasticitySystem> ("Linear Elasticity");
237
238 system.set_dim(dim);
239
240 system.set_fe_type(fe_type);
241
242 // Solve this as a time-dependent or steady system
243 std::string time_solver = infile("time_solver","DIE!");
244
245 ExplicitSystem * v_system = nullptr;
246 ExplicitSystem * a_system = nullptr;
247
248 if (time_solver == "newmark")
249 {
250 // Create ExplicitSystem to help output velocity
251 v_system = &equation_systems.add_system<ExplicitSystem> ("Velocity");
252 v_system->add_variable("u_vel", fe_type);
253 v_system->add_variable("v_vel", fe_type);
254 if (dim == 3)
255 v_system->add_variable("w_vel", fe_type);
256
257 // Create ExplicitSystem to help output acceleration
258 a_system = &equation_systems.add_system<ExplicitSystem> ("Acceleration");
259 a_system->add_variable("u_accel", fe_type);
260 a_system->add_variable("v_accel", fe_type);
261 if (dim == 3)
262 a_system->add_variable("w_accel", fe_type);
263 }
264
265 if (time_solver == "newmark")
266 system.time_solver = std::make_unique<NewmarkSolver>(system);
267
268 else if (time_solver == "euler")
269 {
270 system.time_solver = std::make_unique<EulerSolver>(system);
271 EulerSolver & euler_solver = cast_ref<EulerSolver &>(*(system.time_solver.get()));
272 euler_solver.theta = infile("theta", 1.0);
273 }
274
275 else if (time_solver == "euler2")
276 {
277 system.time_solver = std::make_unique<Euler2Solver>(system);
278 Euler2Solver & euler_solver = cast_ref<Euler2Solver &>(*(system.time_solver.get()));
279 euler_solver.theta = infile("theta", 1.0);
280 }
281
282 else if (time_solver == "steady")
283 {
284 system.time_solver = std::make_unique<SteadySolver>(system);
285 libmesh_assert_equal_to (n_timesteps, 1);
286 }
287 else
288 libmesh_error_msg(std::string("ERROR: invalid time_solver ")+time_solver);
289
290 // Initialize the system
291 equation_systems.init ();
292
293 // Set the time stepping options
294 system.deltat = deltat;
295
296 // And the nonlinear solver options
297 DiffSolver & solver = *(system.time_solver->diff_solver().get());
298 solver.quiet = infile("solver_quiet", true);
299 solver.verbose = !solver.quiet;
300 solver.max_nonlinear_iterations = infile("max_nonlinear_iterations", 15);
301 solver.relative_step_tolerance = infile("relative_step_tolerance", 1.e-3);
302 solver.relative_residual_tolerance = infile("relative_residual_tolerance", 0.0);
303 solver.absolute_residual_tolerance = infile("absolute_residual_tolerance", 0.0);
304
305 // And the linear solver options
306 solver.max_linear_iterations = infile("max_linear_iterations", 50000);
307 solver.initial_linear_tolerance = infile("initial_linear_tolerance", 1.e-3);
308
309 // And the system
310 system.print_element_jacobians = infile("print_element_jacobians", false);
311 system.print_element_solutions = infile("print_element_solutions", false);
312 system.print_element_residuals = infile("print_element_residuals", false);
313 system.print_residuals = infile("print_residuals", false);
314 system.print_jacobians = infile("print_residuals", false);
315 system.print_residual_norms = infile("print_residual_norms", false);
316 system.print_jacobian_norms = infile("print_residuals", false);
317
318 // Print information about the system to the screen.
319 equation_systems.print_info();
320
321 // If we're using EulerSolver or Euler2Solver, and we're using EigenSparseLinearSolver,
322 // then we need to reset the EigenSparseLinearSolver to use SPARSELU because BICGSTAB
323 // chokes on the Jacobian matrix we give it and Eigen's GMRES currently doesn't work.
324 NewtonSolver * newton_solver = dynamic_cast<NewtonSolver *>( &solver );
325 if (newton_solver &&
326 (time_solver == "euler" || time_solver == "euler2"))
327 {
328#ifdef LIBMESH_HAVE_EIGEN_SPARSE
329 LinearSolver<Number> & linear_solver = newton_solver->get_linear_solver();
330 EigenSparseLinearSolver<Number> * eigen_linear_solver =
331 dynamic_cast<EigenSparseLinearSolver<Number> *>(&linear_solver);
332
333 if (eigen_linear_solver )
334 eigen_linear_solver->set_solver_type(SPARSELU);
335#endif
336 }
337
338 if (time_solver == "newmark")
339 {
340 NewmarkSolver * newmark = cast_ptr<NewmarkSolver*>(system.time_solver.get());
341 newmark->compute_initial_accel();
342
343 // Copy over initial velocity and acceleration for output.
344 // Note we can do this because of the matching variables/FE spaces
345 *(v_system->solution) = system.get_vector("_old_solution_rate");
346 *(a_system->solution) = system.get_vector("_old_solution_accel");
347 }
348
349#ifdef LIBMESH_HAVE_EXODUS_API
350 // Output initial state (if we can - IGA meshs don't serialize yet)
351 if (mesh.is_serial() || !use_iga)
352 {
353 std::ostringstream file_name;
354
355 // We write the file in the ExodusII format.
356 file_name << std::string("out.")+time_solver+std::string(".e-s.")
357 << std::setw(3)
358 << std::setfill('0')
359 << std::right
360 << 0;
361
362 ExodusII_IO(mesh).write_timestep(file_name.str(),
363 equation_systems,
364 1, // This number indicates how many time steps
365 // are being written to the file
366 system.time);
367 }
368#endif // #ifdef LIBMESH_HAVE_EXODUS_API
369
370 // Now we begin the timestep loop to compute the time-accurate
371 // solution of the equations.
372 for (unsigned int t_step=0; t_step != n_timesteps; ++t_step)
373 {
374 // A pretty update message
375 libMesh::out << "\n\nSolving time step "
376 << t_step
377 << ", time = "
378 << system.time
379 << std::endl;
380
381 system.solve();
382
383 // Advance to the next timestep in a transient problem
384 system.time_solver->advance_timestep();
385
386 // Copy over updated velocity and acceleration for output.
387 // Note we can do this because of the matching variables/FE spaces
388 if (time_solver == "newmark")
389 {
390 *(v_system->solution) = system.get_vector("_old_solution_rate");
391 *(a_system->solution) = system.get_vector("_old_solution_accel");
392 }
393
394#ifdef LIBMESH_HAVE_EXODUS_API
395 // Write out this timestep if we're requested to
396 // (if we can - IGA meshs don't serialize yet)
397 if ((mesh.is_serial() || !use_iga) &&
398 ((t_step+1)%write_interval == 0))
399 {
400 std::ostringstream file_name;
401
402 // We write the file in the ExodusII format.
403 file_name << std::string("out.")+time_solver+std::string(".e-s.")
404 << std::setw(3)
405 << std::setfill('0')
406 << std::right
407 << t_step+1;
408
409 ExodusII_IO(mesh).write_timestep(file_name.str(),
410 equation_systems,
411 1, // This number indicates how many time steps
412 // are being written to the file
413 system.time);
414 }
415#endif // #ifdef LIBMESH_HAVE_EXODUS_API
416 }
417
418 // All done.
419 return 0;
420}
unsigned int dim
void set_dim(unsigned int dim)
void set_fe_type(const FEType &fe_type)
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.
bool has_boundary_id(const Node *const node, const boundary_id_type id) const
void add_node(const Node *node, const boundary_id_type id)
Add Node node with boundary id id to the boundary information data structures.
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.
void regenerate_id_sets()
Clears and regenerates the cached sets of ids.
This is a generic class that defines a solver to handle ImplicitSystem classes, including NonlinearIm...
Definition diff_solver.h:70
Real absolute_residual_tolerance
The DiffSolver should exit after the residual is reduced to either less than absolute_residual_tolera...
unsigned int max_linear_iterations
Each linear solver step should exit after max_linear_iterations is exceeded.
double initial_linear_tolerance
Any required linear solves will at first be done with this tolerance; the DiffSolver may tighten the ...
bool verbose
The DiffSolver may print a lot more to libMesh::out if verbose is set to true; default is false.
unsigned int max_nonlinear_iterations
The DiffSolver should exit in failure if max_nonlinear_iterations is exceeded and continue_after_max_...
bool quiet
The DiffSolver should not print anything to libMesh::out unless quiet is set to false; default is tru...
bool print_jacobians
Set print_jacobians to true to print J whenever it is assembled.
bool print_residuals
Set print_residuals to true to print F whenever it is assembled.
Real deltat
For time-dependent problems, this is the amount delta t to advance the solution in time.
bool print_element_jacobians
Set print_element_jacobians to true to print each J_elem contribution.
bool print_element_solutions
Set print_element_solutions to true to print each U_elem input.
bool print_residual_norms
Set print_residual_norms to true to print |F| whenever it is assembled.
bool print_element_residuals
Set print_element_residuals to true to print each R_elem contribution.
bool print_jacobian_norms
Set print_jacobian_norms to true to print |J| whenever it is assembled.
std::unique_ptr< TimeSolver > time_solver
A pointer to the solver object we're going to use.
Reading and writing meshes in (a subset of) LS-DYNA format.
Definition dyna_io.h:53
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
This class provides an interface to Eigen iterative solvers that is compatible with the libMesh Linea...
This is the EquationSystems class.
void print_info(std::ostream &os=libMesh::out) const
Prints information about the equation systems, by default to libMesh::out.
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.
This class defines a theta-method (defaulting to Backward Euler with theta = 1.0) solver to handle ti...
Real theta
The value for the theta method to employ: 1.0 corresponds to backwards Euler, 0.0 corresponds to forw...
This class defines a theta-method Euler (defaulting to Backward Euler with theta = 1....
Real theta
The value for the theta method to employ: 1.0 corresponds to backwards Euler, 0.0 corresponds to forw...
The ExodusII_IO class implements reading meshes in the ExodusII file format from Sandia National Labs...
Definition exodusII_io.h:53
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.
Manages consistently variables, degrees of freedom, and coefficient vectors for explicit systems.
virtual void solve() override
Invokes the solver associated with the system.
class FEType hides (possibly multiple) FEFamily and approximation orders, thereby enabling specialize...
Definition fe_type.h:197
OrderWrapper order
The approximation order of the element (at 0 p-refinement level).
Definition fe_type.h:203
FEFamily family
The type of finite element.
Definition fe_type.h:228
The LibMeshInit class, when constructed, initializes the dependent libraries (e.g.
Definition libmesh.h:92
This base class can be inherited from to provide interfaces to linear solvers from different packages...
void set_solver_type(const SolverType st)
Sets the type of solver to use.
virtual bool is_serial() const
Definition mesh_base.h:357
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition mesh_base.h:170
void prepare_for_use(const bool skip_renumber_nodes_and_elements, const bool skip_find_neighbors)
Prepare a newly created (or read) mesh for use.
Definition mesh_base.C:824
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:1755
This is the MeshCommunication class.
void broadcast(MeshBase &) const
This method takes a mesh (which is assumed to reside on processor 0) and broadcasts it to all the oth...
The Mesh class is a thin wrapper, around the ReplicatedMesh class by default.
Definition mesh.h:51
This class defines a Newmark time integrator for second order (in time) DifferentiableSystems.
virtual void compute_initial_accel()
This method uses the specified initial displacement and velocity to compute the initial acceleration ...
This class defines a solver which uses the default libMesh linear solver in a quasiNewton method to h...
LinearSolver< Number > & get_linear_solver()
processor_id_type processor_id() const
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:1344
Real time
For time-dependent problems, this is the time t at the beginning of the current timestep.
Definition system.h:1677
std::unique_ptr< NumericVector< Number > > solution
Data structure to hold solution values.
Definition system.h:1655
const NumericVector< Number > & get_vector(std::string_view vec_name) const
Definition system.C:931
boundary_id_type boundary_id_max_x
boundary_id_type boundary_id_min_y
boundary_id_type boundary_id_max_z
boundary_id_type boundary_id_max_y
boundary_id_type boundary_id_min_z
boundary_id_type boundary_id_min_x
const boundary_id_type fixed_v_boundary_id
const boundary_id_type edge_boundary_id
const boundary_id_type pressure_boundary_id
const boundary_id_type node_boundary_id
const boundary_id_type fixed_u_boundary_id
MeshBase & mesh
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.
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.
The libMesh namespace provides an interface to certain functionality in the library.
SolverPackage default_solver_package()
Definition libmesh.C:1064
OStreamProxy out
@ RATIONAL_BERNSTEIN
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
int main()